docs: add Decentralized Masternode Shares DIP - #187
Conversation
Pre-DIP (alias dip-pasta-shared-masternode-collateral per BIP2 naming) specifying trustless shared masternode collateral: 2-8 participants fund one masternode atomically, consensus splits owner rewards by contribution, and a covenant-enforced dissolution transaction (ProDisTx) is the only way collateral can move. Builds on DIP-0003 and extends DIP-0026 provider transaction version 4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review. 📝 WalkthroughWalkthroughThis PR adds a DIP for trustless shared masternode ownership by 2–8 participants. It defines registration, rewards, dissolution, lifecycle updates, consensus enforcement, activation, tests, security considerations, and supporting vocabulary updates. ChangesShared Masternode Collateral DIP
Estimated code review effort: 4 (Complex) | ~60 minutes Merge Risk: ⚪ Minimal · up to This documentation-only change introduces no actionable merge-blocking risk; it is merge-ready after normal checks and review. Sequence Diagram(s)sequenceDiagram
participant ShareOwners
participant Registration
participant DeterministicList
participant ConsensusValidation
participant RewardConstruction
ShareOwners->>Registration: submit shared registration and consent signatures
Registration->>DeterministicList: create shared masternode state
ShareOwners->>DeterministicList: submit lifecycle update or dissolution transaction
DeterministicList->>ConsensusValidation: validate template, signatures, and state
ConsensusValidation->>RewardConstruction: apply share-table reward rules
RewardConstruction-->>ShareOwners: construct proportional reward outputs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@dip-pasta-shared-masternode-collateral.md`:
- Around line 286-291: The monotonicity note in the ProDisTx/requiredPenalty
description is too strong for the zero-penalty case; rewrite the sentence so
validity is defined by paying at least requiredPenalty, and say a unilateral
ProDisTx is invalid whenever it pays less than requiredPenalty rather than only
“until the early period ends.” Update the surrounding explanation in the section
that discusses requiredPenalty, earlyPenalty, and ProDisTx validity to preserve
the monotonicity claim without excluding earlyPenalty = 0.
- Around line 292-307: The ProDisTx validity rules in the actor/output section
currently allow the actor refund output to be optional whenever it is non-zero,
which can let a valid dissolution omit the actor’s remaining principal. Update
the wording around the `actorIndex`, `shares[a].refundScript`, and
`requiredPenalty` rules to make the actor output mandatory whenever
`shares[a].amount - P > 0`, and only omit it in the genuinely zero-value case;
keep the rest of the output-order and no-extra-outputs constraints unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5f33eb94-07ee-4903-8e91-242fb4c1f4e8
📒 Files selected for processing (2)
dip-pasta-shared-masternode-collateral.mdproject-words.txt
|
<3 |
- Commit the signature count into SharedDisHash so the unilateral/unanimous mode cannot be malleated by dropping or adding signatures after signing. - Remove the non-normative replace-by-higher-fee relay suggestion; Dash relay has no replacement mechanism, so the first ProDisTx for a collateral wins. - Remove operatorReward from the ProUpSharedRegTx payload: the operator reward is fixed at registration, matching the DIP-0003 ProUpRegTx model. - Specify that a validated ProDisTx removes its masternode in the collateral-spend phase of list construction, after the block's other provider transactions apply, so a same-masternode update and dissolution are valid together in one block in either order. - Rewrite the filtering section: the registration matches on every share refund script, reward script and owner key; the lifecycle transactions are matched by proTxHash (plus ProUpShareTx's new reward script), consistent with the DIP-0003 treatment of ProUpRegTx and ProUpRevTx, since they do not carry the share table and stateless filters perform no list lookup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Following up on my #186 closing comment. This is the Platform-side half I said What ranI implemented most of this draft on a fork based on merged DIP-0026 ( On the tested paths, the draft behaves as specified:
On that tested path, nothing is ever pre-signed against a transaction id, so the failure Notes on the draft text from implementing itThese are the places where building it required decisions the text does not currently pin
The Platform-side companionThis is the half I promised when closing the RFC. Everything here ran on a local Platform One property carries the whole design. A single asset-lock transaction can hold one credit On top of that, a small document contract (pool, share, membership request, reward accrual,
Two design notes for anyone building this layer:
Asks
|
…-signer The honesty section now says plainly that dashpay/dips#187 is an open, unmerged proposal (as of July 2026) whose consensus changes would wait on a future hard fork even after a merge, and that the Layer 1 half of this design moves with it. The what-is-here section credits dash-rawkey-signer, the standalone wallet-free signing library the seed-loss recovery path runs on.
Dash Core folded the unreleased ProTx version 4 (MultiPayout) into version 3 (extended addresses) since both are introduced by the same deployment, so the shared-collateral fields now extend the version 3 payload; update every version reference accordingly. The reference implementation also dropped support for registering and dissolving a shared masternode within one block: a ProDisTx is now validated against the previous block's masternode list in blocks exactly as in the mempool, which removes the need for a second block-context validation path. Update the enforcement rules and test vectors to match. A standby dissolution simply becomes valid one block after registration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
dip-pasta-shared-masternode-collateral.md (2)
172-176: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winApply the canonical low-S rule to shared registration update signatures too.
ProUpShareTxandProUpSharedRegTxare signature-bearing payloads introduced by this DIP, but lines 172-174 only listjoinSigsandProDisTx. Since these updates are consensus-critical and affect txid stability, reword this as applying to every signature introduced by the DIP, includingProUpShareTx.payloadSigandProUpSharedRegTx.sigs, or document an explicit exception.Suggested wording
-All signatures introduced by this DIP (`joinSigs` and ProDisTx signatures) are +All signatures introduced by this DIP, including `joinSigs`, ProDisTx, +ProUpShareTx, and ProUpSharedRegTx signatures, are🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dip-pasta-shared-masternode-collateral.md` around lines 172 - 176, Update the low-S signature rule statement to cover every signature introduced by the DIP, explicitly including ProUpShareTx.payloadSig and ProUpSharedRegTx.sigs alongside joinSigs and ProDisTx signatures; do not leave these consensus-critical update signatures as an implicit exception.
204-207: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winMake the payee-reuse predicate explicit.
Line 204 mentions P2PK even though share scripts are described as P2PKH/P2SH and the test cases omit P2PK. Define P2PK as invalid/rejected, and define P2PKH/P2SH reuse as exact destination comparison: match P2PKH
CKeyIDs asHASH160(pubkey)and match P2SHCScriptIDs to the script hash. Without that predicate, implementations can disagree on registration validity.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dip-pasta-shared-masternode-collateral.md` around lines 204 - 207, Clarify the payee-reuse rule in the collateral specification: explicitly reject any P2PK refundScript or rewardScript, and define P2PKH/P2SH reuse as exact destination comparisons, matching P2PKH CKeyIDs against HASH160(pubkey) and P2SH CScriptIDs against the script hash. Update the relevant rule and test cases to use this predicate consistently.
🧹 Nitpick comments (2)
dip-pasta-shared-masternode-collateral.md (2)
631-645: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAdd a wire-format compatibility vector for non-shared version 3
ProRegTx.Lines 112-115 append shared fields to every version-3 payload while claiming
sharesCount = 0remains DIP-0026-compatible. Add an exact serialization and deserialization test coveringpayouts, emptysharesandjoinSigs, and zeroed penalty fields. This protects ordinary version-3 registrations from wire-format regressions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dip-pasta-shared-masternode-collateral.md` around lines 631 - 645, Add a wire-format compatibility test for a non-shared version-3 ProRegTx covering exact serialization and deserialization with populated payouts, empty shares and joinSigs, and zeroed penalty fields; preserve the expected DIP-0026-compatible encoding when sharesCount is zero.
675-677: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winTest both same-block update and dissolution orders.
Lines 472-475 allow
ProUpShareTxorProUpSharedRegTxandProDisTxin either transaction order. Add test vectors for both(update, dissolution)and(dissolution, update)in one block. This verifies that block validation and mempool validation apply the same state transition.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dip-pasta-shared-masternode-collateral.md` around lines 675 - 677, Expand the reorg test vectors to cover both transaction orders within a single block: ProUpShareTx or ProUpSharedRegTx followed by ProDisTx, and ProDisTx followed by the update transaction. Verify each ordering through both block validation and mempool validation, preserving the deterministic masternode snapshots before and after shared-state changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@dip-pasta-shared-masternode-collateral.md`:
- Around line 172-176: Update the low-S signature rule statement to cover every
signature introduced by the DIP, explicitly including ProUpShareTx.payloadSig
and ProUpSharedRegTx.sigs alongside joinSigs and ProDisTx signatures; do not
leave these consensus-critical update signatures as an implicit exception.
- Around line 204-207: Clarify the payee-reuse rule in the collateral
specification: explicitly reject any P2PK refundScript or rewardScript, and
define P2PKH/P2SH reuse as exact destination comparisons, matching P2PKH CKeyIDs
against HASH160(pubkey) and P2SH CScriptIDs against the script hash. Update the
relevant rule and test cases to use this predicate consistently.
---
Nitpick comments:
In `@dip-pasta-shared-masternode-collateral.md`:
- Around line 631-645: Add a wire-format compatibility test for a non-shared
version-3 ProRegTx covering exact serialization and deserialization with
populated payouts, empty shares and joinSigs, and zeroed penalty fields;
preserve the expected DIP-0026-compatible encoding when sharesCount is zero.
- Around line 675-677: Expand the reorg test vectors to cover both transaction
orders within a single block: ProUpShareTx or ProUpSharedRegTx followed by
ProDisTx, and ProDisTx followed by the update transaction. Verify each ordering
through both block validation and mempool validation, preserving the
deterministic masternode snapshots before and after shared-state changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5f26b49d-0a44-42b9-837c-9533c2c872c7
📒 Files selected for processing (1)
dip-pasta-shared-masternode-collateral.md
BIP68 gives input sequence numbers consensus meaning on version 2 and later transactions, so a sequence rewrite between consent signing and funding-input signing could impose a months-long relative timelock on a fully consented registration without invalidating any consent signature. Hash the sequences into SharedRegConsentHash, matching what SharedDisHash already does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
thephez
left a comment
There was a problem hiding this comment.
@coderabbitai @PastaPastaPasta please see feedback
thephez
left a comment
There was a problem hiding this comment.
Few more items to consider
The minimum-based output rules left two unbounded paths by which value could leave the actor's share: the transaction fee and voluntary penalty overpayment. A single stolen share owner key could sign a unilateral ProDisTx that pays the victim's entire share to miners as fee, or as bonus concentrated on a colluding participant, contradicting the guarantee that owner-key compromise can never cost principal. Cap the fee at a fixed constant (MAX_DIS_FEE, 0.01 DASH) in both modes and cap the unilateral bonus sum at the configured earlyPenalty. Both ceilings are height-independent, so monotone validity is preserved and early-period standbys paying the full penalty stay valid forever. Worst-case loss under key compromise is now earlyPenalty + MAX_DIS_FEE. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two digests were defined in prose, leaving byte-level choices open that would fork independent implementations: whether the domain tags are hashed as bare bytes or as length-prefixed strings, whether the shares field includes the sharesCount byte, and what 'netInfo and Platform fields' covers for a type-0 payload. Pin all three, note why collateralOutpoint is deliberately absent, and state the signed-hash convention for ProUpShareTx and ProUpSharedRegTx (signature fields omitted entirely, per DIP-0003 practice), including why sigCount is uncommitted for type 12 but committed for ProDisTx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The requirement that a sharesCount = 0 payload carry no join signatures and zeroed penalty fields was only a descriptive parenthetical; no listed validation rule rejected a non-shared payload with stray non-zero values, so implementations could disagree on block validity. State the rule normatively and add it to the test list. Also rewords the section to stop describing version 3 as unreleased, which would go stale on release (review feedback). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Registration rule 10 and ProUpShareTx both prevent a share script from paying the voting key, but the collision has a third entry point the spec did not cover: keeping the scripts fixed and moving the voting key onto an existing payee via ProUpSharedRegTx. The reference implementation already rejects this; state the rule so independent implementations agree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The spec fixed same-block semantics only for ProDisTx, leaving two block-validity-observable orderings to implementer inference: whether an update may follow its masternode's registration in the same block (yes, updates validate against the evolving in-block list) and whether a registration may reuse an owner key freed by a same-block dissolution (no, removal takes effect in the collateral-spend phase). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The dissolution digest commits nLockTime and the input sequence but consensus does not restrict them, and BIP68 applies to version-3 transactions, so a co-signer's wallet could embed a lock the other signers fail to notice. Keep time locks available to participants who want them, scope the monotone-validity claim to final transactions, and require wallets to verify the lock fields before signing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback: statements that version 3 is unreleased go stale the moment it ships. The deploy-together requirement stands on its own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Pushed 7 commits addressing an internal adversarial review plus the outstanding review feedback: Security fix — Interoperability pins (each of these was a place an independent implementation could diverge from the reference implementation and fork):
Guidance and wording:
Test Cases updated to cover each new rule. The PR description's stale "version 4" reference is also fixed. The reference implementation (dashpay/dash#7437) is being updated to match the new ceilings. 🤖 Posted autonomously by Claude on behalf of pasta. |
Decentralized Masternode Shares (pre-DIP)
Adds
dip-pasta-shared-masternode-collateral.md(alias naming per BIP2 conventions, pending DIP number assignment): a specification for trustless shared masternode ownership — 2 to 8 participants fund one masternode's collateral atomically in a single registration transaction, consensus splits the owner reward by recorded contribution, and the collateral can only move through a consensus-enforced dissolution transaction that pays each participant's principal to a refund script fixed at registration.Relationship to DIP-0026 / #184
This is a strict superset of multi-party payouts: DIP-0026 gives a protocol-enforced recurring reward split but leaves the registrar in control of the payout list and the collateral spendable by whoever holds it — its Security Considerations explicitly defer immutable payout rights to a future protocol mechanism. This DIP is that mechanism. It extends the provider transaction payload version 3 (extended addresses) from #184; a version 3 payload with an empty share list behaves exactly as DIP-0026 specifies. The two deploy together in v24 (EHF).
Key design points
04445348437551, exact match) — creation is valid only inside a shared registration, spending only viaProDisTx; the DIP states the anyone-can-spend-on-non-enforcing-chains trade-off explicitly.MAX_DIS_FEE, 0.01 DASH) and a bonus ceiling at the configuredearlyPenalty, so a stolen share owner key can cost its holder at most the consented early-exit penalty plus a small constant; every other duff must reach the immutable refund script.A reference implementation exists at dashpay/dash#7437 and is kept in sync with this text.
🤖 Generated with Claude Code
Summary by CodeRabbit