From 29810a3cbed75eba776f00afbac0bfc5a0f3f5c3 Mon Sep 17 00:00:00 2001 From: pasta Date: Sat, 4 Jul 2026 17:12:24 -0500 Subject: [PATCH 01/12] docs: add Decentralized Masternode Shares DIP 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 --- dip-pasta-shared-masternode-collateral.md | 708 ++++++++++++++++++++++ project-words.txt | 13 +- 2 files changed, 720 insertions(+), 1 deletion(-) create mode 100644 dip-pasta-shared-masternode-collateral.md diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md new file mode 100644 index 00000000..fa987e8c --- /dev/null +++ b/dip-pasta-shared-masternode-collateral.md @@ -0,0 +1,708 @@ +
+  DIP: pasta-shared-masternode-collateral
+  Title: Decentralized Masternode Shares
+  Author(s): Pasta
+  Comments-Summary: No comments yet.
+  Status: Draft
+  Type: Standard
+  Created: 2026-07-04
+  Requires: 2, 3, 26
+  License: MIT License
+
+ +## Table of Contents + +1. [Abstract](#abstract) +2. [Motivation](#motivation) +3. [Prior Work](#prior-work) +4. [Specification](#specification) + 1. [Terminology](#terminology) + 2. [Provider Transaction Version](#provider-transaction-version) + 3. [Shared Collateral Script](#shared-collateral-script) + 4. [Collateral Share](#collateral-share) + 5. [Registering a Shared Masternode](#registering-a-shared-masternode) + 6. [Dissolving a Shared Masternode (ProDisTx)](#dissolving-a-shared-masternode-prodistx) + 7. [Updating a Share (ProUpShareTx)](#updating-a-share-proupsharetx) + 8. [Updating the Shared Registrar (ProUpSharedRegTx)](#updating-the-shared-registrar-proupsharedregtx) + 9. [Masternode List State](#masternode-list-state) + 10. [Masternode Reward Payments](#masternode-reward-payments) + 11. [Collateral Spend Enforcement](#collateral-spend-enforcement) + 12. [Forbidden Template Destinations](#forbidden-template-destinations) + 13. [Special Transaction Filtering](#special-transaction-filtering) +5. [Deployment and Compatibility](#deployment-and-compatibility) +6. [Rationale](#rationale) +7. [Test Cases](#test-cases) +8. [Security Considerations](#security-considerations) +9. [Privacy Considerations](#privacy-considerations) +10. [Copyright](#copyright) + +## Abstract + +This DIP extends [DIP-0003: Deterministic Masternode Lists](dip-0003.md) and +[DIP-0026: Masternode Multi-Party Payouts](dip-0026.md) to allow 2 to 8 +participants to trustlessly co-own a single masternode. The participants fund +the collateral atomically in one registration transaction, consensus splits the +owner reward across the participants in proportion to their recorded +contributions, and the collateral can leave the masternode only through a +consensus-enforced dissolution that pays each participant's principal to a +refund script recorded at registration. No participant, operator, miner, or +compromised update path can redirect another participant's principal, and no +participant can be prevented from exiting. + +## Motivation + +DIP-0026 provides protocol-enforced recurring reward splitting, but it +explicitly leaves the registrar owner in control of the payout list and does +not protect the collateral UTXO from unilateral spending. Its Security +Considerations section notes that "a share arrangement that requires immutable +payout rights must use additional contractual, wallet, or protocol mechanisms +outside the scope of this DIP." This DIP is that protocol mechanism. + +Without it, shared masternode ownership requires trusting a collateral holder: +whoever controls the collateral key can spend the collateral, destroy the +masternode, and abscond with the other participants' principal. Pre-signed +multi-signature refund transactions cannot fix this on Dash: without SegWit or +Taproot, a co-signer can malleate the funding transaction identifier and +invalidate every pre-signed child. The only robust construction available is a +consensus-enforced covenant: the protocol itself records each participant's +contribution, refund script, reward script, and owner key, and enforces +refunds, rewards, and update consent directly. + +This DIP is therefore a strict superset of DIP-0026: + +* DIP-0026: protocol-enforced recurring reward split; +* this DIP: protocol-enforced funding, reward split, update consent, and + trustless exit. + +## Prior Work + +* [DIP-0002: Special Transactions](dip-0002.md) +* [DIP-0003: Deterministic Masternode Lists](dip-0003.md) +* [DIP-0026: Masternode Multi-Party Payouts](dip-0026.md) + +## Specification + +### Terminology + +| Term | Definition | +| --- | --- | +| Participant | One of the 2 to 8 co-owners of a shared masternode. | +| Share | One participant's recorded collateral contribution and associated scripts and key. | +| Share table | The ordered list of shares recorded at registration. Order is consensus-significant. | +| Refund script | The immutable script a participant's principal is paid to at dissolution. | +| Reward script | The script a participant's portion of owner rewards is paid to. Participant-updatable. | +| Share owner key | The ECDSA key (as a key ID) that authorizes a participant's consensus actions. Immutable. | +| Early period | The first `earlyPeriodBlocks` blocks after registration, during which unilateral exit is penalized. | +| Dissolution | The consensus-enforced spend of shared collateral that refunds all participants and removes the masternode. | +| Actor | The participant designated by `actorIndex` in a dissolution; pays the penalty (if any) and the transaction fee. | +| Unilateral dissolution | A dissolution authorized by exactly one participant. | +| Unanimous dissolution | A dissolution authorized by every participant. | + +### Provider Transaction Version + +This DIP extends the version 4 (`MultiPayout`) ProRegTx payload defined in +DIP-0026, prior to its release in any Dash Core version. A version 4 ProRegTx +with a non-zero `sharesCount` is a **shared registration**. A version 4 +ProRegTx with `sharesCount = 0` behaves exactly as specified in DIP-0026. + +Because version 4 has not been released, the additional fields below change the +serialization of all version 4 ProRegTx payloads (a non-shared payload still +serializes `sharesCount = 0`, an empty share list, and zeroed penalty fields). +This DIP and DIP-0026 must therefore deploy in the same release. + +### Shared Collateral Script + +The collateral output of a shared registration must use exactly the following +7-byte script (the **template**): + +```text +0x04 "DSHC" OP_DROP OP_TRUE +hex: 04445348437551 +``` + +Template matching is always **exact-script** comparison against these 7 bytes, +never prefix matching. + +The template contains no keys and is spendable with an empty `scriptSig` at the +script layer. All protection comes from the following consensus rules, active +from deployment activation: + +* **Creation:** an output whose `scriptPubKey` equals the template is valid + only as the collateral output of a valid shared registration. Any other + creation — in a normal transaction, a coinbase, another special transaction, + or a non-collateral output of the registration itself — is invalid. +* **Spending:** a transaction spending an output whose `scriptPubKey` equals + the template is valid only if it is a valid ProDisTx whose referenced + masternode owns that outpoint. + +A template output mined before activation (necessarily created deliberately, +against a pattern reserved by this published DIP, and nonstandard to relay) +becomes permanently unspendable at activation. This follows the established +reserved-pattern upgrade path and affects only coins whose owner explicitly +opted in. + +### Collateral Share + +Each share has the following structure: + +| Field | Type | Size | Description | +| --- | --- | --- | --- | +| amount | int64_t | 8 | Collateral contribution in duffs. | +| refundScriptSize | compactSize uint | 1-9 | Size of the refund script. | +| refundScript | Script | Variable | Immutable principal refund script (P2PKH/P2SH). | +| rewardScriptSize | compactSize uint | 1-9 | Size of the reward script. | +| rewardScript | Script | Variable | Reward payout script (P2PKH/P2SH). Zero length means "use refundScript". | +| ownerKeyID | CKeyID | 20 | Immutable share owner key ID. | + +For shared registrations, the following fields are appended to the version 4 +ProRegTx payload immediately after the `payouts` field defined in DIP-0026: + +| Field | Type | Size | Description | +| --- | --- | --- | --- | +| sharesCount | uint8_t | 1 | Number of shares. 0 = not shared; otherwise 2 to 8. | +| shares | CollateralShare[] | Variable | The share table, in consensus-significant order. | +| joinSigs | signature[sharesCount] | 65 * sharesCount | One registration consent signature per share, in share order. | +| earlyPeriodBlocks | uint32_t | 4 | Length of the early period in blocks. | +| earlyPenalty | int64_t | 8 | Penalty in duffs for unilateral dissolution during the early period. | + +All signatures introduced by this DIP (`joinSigs` and ProDisTx signatures) are +65-byte compact recoverable ECDSA signatures with **enforced low-S** (the +signature scheme used for owner-key payload signatures in DIP-0003). High-S +signatures are invalid. This, combined with the digest rules below, makes the +affected transaction identifiers non-malleable by third parties. + +### Registering a Shared Masternode + +A shared registration (version 4 ProRegTx, `sharesCount > 0`) is valid only if +all of the following hold, in addition to DIP-0003 rules not explicitly +replaced here: + +1. The deployment (see [Deployment and Compatibility](#deployment-and-compatibility)) + is active. +2. `type` is 0 (Regular masternode). Shared Evo masternodes are out of scope + (see [Rationale](#rationale)). +3. The collateral is internal: `collateralOutpoint.hash` is null and + `collateralOutpoint.n` points at an output of this transaction whose value + equals the required collateral and whose `scriptPubKey` is exactly the + template. +4. The DIP-0026 `payouts` list is empty (owner rewards derive from the share + table) and the legacy `scriptPayout` is absent per version 4. +5. `keyIdOwner` is all zeros. The share owner keys replace it. +6. `sharesCount` is between 2 and 8 inclusive. +7. Every share `amount` is at least 100 DASH, and the amounts sum exactly to + the required collateral. +8. `0 <= earlyPenalty < min(share amounts)`, and `earlyPeriodBlocks` does not + exceed 420480 blocks (approximately two years at 2.5-minute blocks). +9. No two shares have the same `ownerKeyID`; no share `ownerKeyID` equals any + owner key registered by any other masternode (see + [Masternode List State](#masternode-list-state)); duplicate `refundScript`s + within the share table are invalid; duplicate `rewardScript`s are allowed. +10. No `refundScript` or `rewardScript` pays P2PK or P2PKH to any share + `ownerKeyID` in the table or to `keyIdVoting` (extends the DIP-0026 + payee-reuse rules), and none equals the template + (see [Forbidden Template Destinations](#forbidden-template-destinations)). +11. The external-collateral `payloadSig` is empty, and each entry of `joinSigs` + is a valid canonical signature by the corresponding share's `ownerKeyID` + over `SharedRegConsentHash`. + +`SharedRegConsentHash` is the double-SHA256 of the serialization of: + +```text +"DashSharedMNReg" || payload version || +tx version || tx type || tx nLockTime || +inputsHash || outputsHash || +type || mode || netInfo and Platform fields || +keyIdVoting || pubKeyOperator || operatorReward || +shares || earlyPeriodBlocks || earlyPenalty +``` + +where `inputsHash` and `outputsHash` are as defined in DIP-0003, and `shares` +is the serialized share table. Consent therefore binds every participant to the +exact funding inputs, all outputs (including the collateral output and every +change output), the full share table, the penalty terms, and the registrar +configuration. Consent never relies on the sighash modes of funding-input +signatures, because Dash transaction signatures permit `SIGHASH_NONE`, +`SIGHASH_SINGLE`, and `SIGHASH_ANYONECANPAY`. + +Registration is atomic: if any funding input is double-spent or any participant +withholds a `joinSig`, no shared masternode is created and no participant's +funds move. Co-signer transaction-identifier malleability is harmless: nothing +is pre-signed against the registration txid, and the consent digest binds to +prevouts and outputs, which malleation cannot change. + +### Dissolving a Shared Masternode (ProDisTx) + +This DIP introduces a new special transaction type: + +| Name | Special transaction type | +| --- | --- | +| ProDisTx | 10 | + +Payload: + +| Field | Type | Size | Description | +| --- | --- | --- | --- | +| version | uint16_t | 2 | Payload version. Currently 1. | +| proTxHash | uint256 | 32 | The ProRegTx hash of the shared masternode. | +| actorIndex | uint16_t | 2 | Index into the share table of the actor. | +| sigCount | uint8_t | 1 | Number of signatures: exactly 1 (unilateral) or exactly `sharesCount` (unanimous). | +| sigs | signature[sigCount] | 65 * sigCount | Canonical signatures over `SharedDisHash`. | + +There is no mode field: **the signature count defines the mode**. Exactly one +signature — which must be by `shares[actorIndex].ownerKeyID` — is a unilateral +dissolution. Exactly `sharesCount` signatures — one per share, in share order — +is a unanimous dissolution. Any other count, order, or signer set is invalid. + +A ProDisTx must have exactly one input: the masternode's collateral outpoint, +with an empty `scriptSig`. It must have no outputs other than those required +below. + +`SharedDisHash` is the double-SHA256 of the serialization of: + +```text +"DashSharedMNDissolve" || payload version || +tx version || tx type || tx nLockTime || +all input prevouts || all input sequences || all outputs || +proTxHash || actorIndex +``` + +The digest commits to the transaction's actual input and outputs directly; the +payload deliberately carries no `inputsHash`/`outputsHash` copies. + +#### Required penalty + +```text +early = (spendHeight - registeredHeight) < earlyPeriodBlocks +requiredPenalty = (unanimous or not early) ? 0 : earlyPenalty +``` + +`spendHeight` is the height of the block containing the ProDisTx and +`registeredHeight` is the masternode's registration height. `requiredPenalty` +can only step from `earlyPenalty` to zero as the chain advances, so it is +non-increasing in `spendHeight`. + +#### Output rules + +All output rules are **minimum-based**: paying more penalty than required is +always valid. Because `requiredPenalty` is non-increasing, a ProDisTx that is +valid at height `h` is valid at every height `h' >= h`: no dissolution is +silently invalidated by chain progress. The converse is intentionally not true: +a unilateral ProDisTx paying no penalty is invalid until the early period ends. + +Let `a = actorIndex`, `W` = the sum of non-actor share amounts, and +`P = requiredPenalty`. A ProDisTx is valid only if: + +1. `actorIndex < sharesCount`. +2. There is exactly one output per non-actor share `i`, paying + `shares[i].refundScript`, in share order, plus optionally one final output + paying `shares[a].refundScript` (the actor output). A zero-value actor + output must be omitted. No other outputs are permitted. +3. For each non-actor output, with `bonus[i] = value[i] - shares[i].amount`: + `bonus[i] >= floor(P * shares[i].amount / W)`. +4. The sum of all `bonus[i]` is at least `P`. + +Everything not paid to these outputs is the transaction fee, which by value +conservation can only come from the actor's share; the actor output is +therefore implicitly capped at `shares[a].amount - P`. + +The per-recipient minimum in rule 3 uses a plain floor with no remainder +assignment: floors are element-wise monotone in `P`, whereas any remainder +assignment rule is not, and would break monotone validity. Rule 4 forces the +sub-duff remainder onto some non-actor output. The pro-rata floors also prevent +the actor from concentrating the penalty into a colluding share beyond rounding +dust. Constructors should build outputs with the same sequential-floor, +remainder-to-last convention that DIP-0026 uses for rewards; that construction +always satisfies the minimums. + +When a valid ProDisTx is confirmed, the masternode is removed from the +deterministic masternode list. Confirmed reward-script or registrar updates +never invalidate a pending ProDisTx (its digest references none of those +fields); the only transaction that can displace a pending ProDisTx is a +competing ProDisTx, which conflicts on the collateral input. + +#### Fees and standby dissolutions (non-normative) + +Dash relay supports neither BIP125 replacement nor package relay, so a ProDisTx +must embed a sufficient fee at signing time; child-pays-for-parent on the actor +output can only raise the mining priority of a ProDisTx that already meets the +minimum relay fee. A flat fee of roughly 100000 duffs — about one millionth of +the minimum share — provides large fee-market headroom. Relay policy may allow +a ProDisTx to replace a pending ProDisTx spending the same collateral when it +pays a strictly higher fee. + +Because validity is monotone and share owner keys are immutable, a signed +ProDisTx never goes stale. Wallets should have each participant sign a +**standby dissolution** immediately after registration confirms — unilateral, +paying `earlyPenalty` (valid immediately and forever) or no penalty (valid from +the end of the early period, forever) — and store it with the participant's +refund-key backup, separately from the owner key. If the owner key is later +lost, broadcasting the standby recovers the participant's principal without any +other party's cooperation. A leaked standby is griefing-only: every output is +covenant-fixed, so its holder can trigger the exit (costing the signer the +penalty) but cannot redirect any value. + +### Updating a Share (ProUpShareTx) + +| Name | Special transaction type | +| --- | --- | +| ProUpShareTx | 11 | + +Payload: + +| Field | Type | Size | Description | +| --- | --- | --- | --- | +| version | uint16_t | 2 | Payload version. Currently 1. | +| proTxHash | uint256 | 32 | The ProRegTx hash of the shared masternode. | +| shareIndex | uint16_t | 2 | Index into the share table of the share being updated. | +| rewardScriptSize | compactSize uint | 1-9 | Size of the new reward script. | +| rewardScript | Script | Variable | New reward script (P2PKH/P2SH), or zero length for "use refundScript". | +| inputsHash | uint256 | 32 | Hash of all transaction inputs, as in DIP-0003 update payloads. | +| payloadSig | signature | 65 | Canonical signature by `shares[shareIndex].ownerKeyID` over the payload hash (with this field empty). | + +A ProUpShareTx updates exactly one share's `rewardScript` and nothing else. +Share `amount`, `refundScript`, `ownerKeyID`, the participant count, the +penalty, and the early period are immutable for the life of the masternode. The +new reward script is subject to the same script-type, payee-reuse, and template +restrictions as at registration. A ProUpShareTx referencing a non-shared +masternode is invalid. + +### Updating the Shared Registrar (ProUpSharedRegTx) + +| Name | Special transaction type | +| --- | --- | +| ProUpSharedRegTx | 12 | + +Payload: + +| Field | Type | Size | Description | +| --- | --- | --- | --- | +| version | uint16_t | 2 | Payload version. Currently 1. | +| proTxHash | uint256 | 32 | The ProRegTx hash of the shared masternode. | +| pubKeyOperator | BLSPubKey | 48 | New operator public key. | +| keyIdVoting | CKeyID | 20 | New voting key ID. | +| operatorReward | uint16_t | 2 | New operator reward in basis points (0 to 10000). | +| inputsHash | uint256 | 32 | Hash of all transaction inputs, as in DIP-0003 update payloads. | +| sigCount | uint8_t | 1 | Must equal `sharesCount`. | +| sigs | signature[sigCount] | 65 * sigCount | Canonical signatures over the payload hash (with this field empty), one per share, in share order. | + +A ProUpSharedRegTx updates the whole-masternode registrar fields and requires +one valid signature from **every** current share owner key, in share order. +Operator-key change semantics (service reset) match ProUpRegTx in DIP-0003. A +ProUpSharedRegTx referencing a non-shared masternode is invalid, and a plain +ProUpRegTx referencing a shared masternode is invalid. + +Operator-authorized updates are unchanged: the operator continues to manage +service fields via ProUpServTx (including the operator payout script) and may +revoke via ProUpRevTx. The operator can stop service but cannot spend +collateral, redirect owner rewards, or rewrite ownership. + +### Masternode List State + +Deterministic masternode state for a shared masternode must store the share +table (without `joinSigs`, which are needed only at registration validation), +`earlyPeriodBlocks`, and `earlyPenalty`. State diffs must report the share +table as a single logical field: any change to a mutable share field includes +the full replacement share table. + +Owner-key uniqueness is enforced across the whole masternode list in both +directions: a share `ownerKeyID` must not equal any other masternode's +registered owner key (shared or `keyIdOwner`), and a normal registration must +not reuse any active share owner key. + +Share data is excluded from the simplified masternode list entry hash +(`CSimplifiedMNListEntry`), matching DIP-0026's treatment of payout data. +Extended JSON forms may expose shares for diagnostics; this is not a +light-client commitment. Full nodes validate shared rewards and dissolutions +from deterministic masternode state reconstructed from blocks. + +### Masternode Reward Payments + +Reward payment construction for a shared masternode follows the DIP-0026 +procedure exactly, with the owner payout entries derived from the share table: + +* the entry ordering is the share-table order; +* the entry weight is the share `amount` (instead of basis points), so entry + `i` (except the last) receives `floor(ownerReward * amount[i] / collateral)` + and the last entry receives the remainder; +* the entry destination is the share's `rewardScript` if non-empty, otherwise + its `refundScript`; +* zero-amount outputs are omitted, and operator reward handling is unchanged. + +This reuses DIP-0026's single rounding convention (sequential floor, remainder +to the last entry) rather than introducing a second one. + +### Collateral Spend Enforcement + +Template rules cannot live only inside provider-transaction checks. Consensus +implementations must enforce, at both mempool acceptance and block connection: + +1. **Spend check:** for every transaction, if any input's previous output + script equals the template, the transaction must be a valid ProDisTx + spending that outpoint. This is a constant exact-script comparison per + input; the masternode list is consulted only inside ProDisTx validation. +2. **Creation check:** any transaction output whose script equals the template, + outside the collateral slot of a valid shared registration, is invalid. +3. Block-level validation evaluates ProDisTx against the deterministic + masternode list as of the previous block plus earlier transactions in the + same block (a masternode may be registered and dissolved within one block). +4. Masternode removal for a shared masternode occurs only through a validated + ProDisTx. Collateral-spend removal semantics for normal masternodes are + unchanged (normal collateral never carries the template). + +Mempool implementations should additionally evict pending ProUpShareTx and +ProUpSharedRegTx transactions for a masternode when its ProDisTx confirms. + +### Forbidden Template Destinations + +Because template outputs may only be created inside shared registrations, any +consensus-mandated output paying the template would be unconstructable and +could deadlock consensus. Validation must therefore reject the template script +wherever a user-supplied script later becomes a consensus-mandated output: + +* share `refundScript` and `rewardScript` values (registration and + ProUpShareTx) — a template refund script would make every dissolution + invalid, freezing the collateral forever; +* every provider-transaction payout script for every masternode type and + payload version, including DIP-0026 payout entries and the operator payout + script — a template payout would make the coinbase unconstructable when that + masternode is paid; +* governance proposal and trigger payment scripts (validated after + activation) — a template payee would invalidate the superblock carrying it; +* asset-unlock (credit withdrawal) destinations — a template destination would + make the withdrawal transaction invalid. + +### Special Transaction Filtering + +Special transaction filters and bloom filters must include every share +`refundScript`, `rewardScript`, and `ownerKeyID` used in a shared registration, +ProDisTx, ProUpShareTx, or ProUpSharedRegTx, in the same manner that DIP-0026 +requires for payout scripts. + +## Deployment and Compatibility + +This DIP deploys with Dash Core's v24 network upgrade (`DEPLOYMENT_V24`, +an Enhanced Hard Fork per [DIP-0023](dip-0023.md)), together with DIP-0026. + +Before activation: + +* Shared registrations, ProDisTx, ProUpShareTx, and ProUpSharedRegTx are + invalid. +* Creating template-scripted outputs is nonstandard but not consensus-invalid. + +After activation: + +* All rules in this DIP apply. +* Template outputs created before activation are permanently unspendable. +* Existing masternodes (legacy and DIP-0026 version 4 non-shared) are + unaffected. Normal masternode collateral-spend semantics are unchanged. + +Shared registration is restricted to Regular masternodes in this version. +Extending shared ownership to Evo masternodes requires a Platform-side +specification for distributing Platform credits according to the share table; +without it, Platform-side rewards would not follow the consensus split and +would reintroduce the trust this DIP removes. + +## Rationale + +### Why a consensus covenant instead of multi-signature scripts + +Dash lacks SegWit and Taproot, so transaction identifiers are malleable by +co-signers and pre-signed refund transactions are unsafe. A P2SH multi-sig +collateral script would additionally freeze funds if signers disappear and +cannot express per-participant refund amounts. Recording ownership in the +provider payload and enforcing spends in consensus — the same trust model as +DIP-0003's deterministic masternode lists — avoids both problems. The price is +that the collateral output is anyone-can-spend at the script layer on any chain +that does not enforce these rules; see +[Security Considerations](#security-considerations). + +### Why the template is pattern-restricted on both creation and spend + +Restricting creation makes the template a trustworthy marker: after activation, +every template output is live shared collateral. Restricting spending by +pattern (rather than by masternode-list lookup) makes the per-input hook a +constant script comparison and closes every path to such an output. Freezing +the (deliberately created, nonstandard) pre-activation outputs follows the +SegWit reserved-pattern precedent and affects only opt-in coins. Exact-script +matching keeps the reserved set to a single 7-byte script. + +### Why share owner keys cannot be rotated + +Rotation authorization would rest on the very key being replaced, so under key +compromise rotation is first-mover-wins: a thief who rotates first locks the +honest participant out of their own share permanently. With immutable keys, +compromise is a standoff in which the honest holder always retains the right to +dissolve, and principal is protected by the immutable refund script in every +scenario. Share owner keys sign only rare deliberate events and should be held +in cold storage; custody migration uses a penalty-free unanimous dissolution +followed by re-registration. Immutability also makes dissolution validity +unconditional: no confirmed transaction can stale a pending ProDisTx's +signatures. + +### Why dissolution outputs are minimum-based + +Exact-amount output rules would make a unilateral ProDisTx signed near the +early-period boundary silently consensus-invalid one block later (the required +penalty changes with height, so the exact amounts change). Minimum-based rules +with a non-increasing required penalty make validity monotone: once valid, +always valid. This property is also what makes standby dissolutions safe to +sign years in advance. + +### Why the penalty is redistributed rather than burned or waived + +Redistribution compensates remaining participants for losing their node and +payment-queue position. Burning would punish the innocent majority to deny a +hypothetical griefer a small bonus. A penalty waiver triggered by sustained +PoSe ban was considered and rejected: the operator controls PoSe status, and in +practice a participant is often the operator, so a waiver lets a participant +deliberately get the node banned and exit penalty-free during the early period, +converting "the leaver compensates the others" into "everyone loses". There is +exactly one penalty and it applies only to unilateral exits during the early +period; free exit afterwards is structural. + +### Why the mode is derived from the signature count + +A ProDisTx with one signature is unilateral; with all `sharesCount` signatures +it is unanimous. An explicit mode byte would be redundant with the signature +count and require a consistency cross-check. Both modes share one output-rule +regime because the minimum-based rules degenerate cleanly to +"each participant receives at least their share" when the required penalty is +zero. + +### Why this extends provider transaction version 4 + +DIP-0026's version 4 has not shipped, so folding shared collateral into the +same version avoids an additional version solely for field layering, at the +cost of a wire-format change to an unreleased payload version. The two DIPs +deploy together in v24. + +### Bounds + +Two to eight participants matches the DIP-0026 payout-entry cap and bounds +coinbase fan-out and masternode state size. The 100 DASH minimum share keeps +shares meaningful and bounds fragmentation. The two-year cap on the early +period prevents unbounded penalty windows. `earlyPenalty < min(share amounts)` +guarantees the actor a positive remainder, so a dissolution can always pay its +own fee. + +## Test Cases + +Implementations should include tests for at least the following: + +1. A valid shared registration with 2 and with 8 participants. +2. Invalid: shared Evo registration; external shared collateral; share sum not + equal to the collateral; share below 100 DASH; `earlyPenalty >= min(share)`; + early period above the cap; non-empty DIP-0026 `payouts` with shares; + non-zero `keyIdOwner`. +3. Invalid: duplicate share owner key within the table or across masternodes + (both directions with normal masternodes); duplicate refund scripts; + refund or reward script paying P2PKH to any share owner key or the voting + key; refund or reward script equal to the template. +4. Invalid: missing or non-canonical (high-S) `joinSig`; consent digest + mismatch after changing any covered field. +5. Invalid: any normal transaction spending a template output; post-activation + template-output creation in a normal transaction, a coinbase, or a + non-collateral output of a shared registration. A near-miss script (template + plus or minus a byte) is unrestricted. A pre-activation template output is + permanently unspendable. +6. Valid: unilateral dissolution during the early period paying `earlyPenalty`; + unilateral dissolution after the early period paying no penalty; a + dissolution signed during the early period confirming after the boundary + (monotonicity); overpaying the penalty; unanimous dissolution paying no + penalty at any height. +7. Invalid: unilateral dissolution paying no penalty inside the early period; + any per-recipient floor violation; bonus sum below the required penalty; + redirected or reordered refund outputs; extra outputs; extra inputs; + non-empty `scriptSig` on the collateral input; wrong actor signature; + signature count other than 1 or `sharesCount`; unanimous signatures out of + share order. +8. Registration and dissolution of the same masternode within one block. +9. A pending dissolution remains valid across confirming ProUpShareTx and + ProUpSharedRegTx transactions; a standby dissolution signed at registration + broadcasts successfully after the early-period boundary. +10. ProUpShareTx updates exactly one reward script with the correct single + signature; invalid for wrong signer, non-shared masternode, or any attempt + to alter immutable fields. ProUpSharedRegTx requires all signatures in + share order; plain ProUpRegTx is invalid for a shared masternode. +11. Reward split across mined blocks: conservation, remainder to the last + entry, zero-output omission, operator reward interaction, reward script + fallback to refund script. +12. Reorg across registration, across the early-period boundary, and across + update-then-dissolution ordering; restart from a deterministic masternode + list snapshot before and after shared state changes. +13. Filter matching for every share refund script, reward script, and owner + key. + +## Security Considerations + +**Non-enforcing chains.** The template is anyone-can-spend at the script layer. +On any chain that does not enforce this DIP — non-upgraded software, a chain +split, a hypothetical future consensus regression — shared collateral outputs +are freely spendable. The protection is the hard fork itself: theft can only +"succeed" on chains that are economically meaningless to the participants. This +trade-off is inherent to consensus covenants on a chain without script-level +covenant support and must be accepted with eyes open. + +**Key separation.** The share owner key is control plane, never a fund +destination. The central guarantee — owner-key compromise cannot steal +principal — holds only because the refund destination is independent of the +owner key: a thief who dissolves as the victim still pays the principal to a +script they cannot spend. This is why refund and reward scripts must not pay to +share owner keys, and why wallets must not derive refund destinations from +owner keys even in forms consensus cannot detect (such as inside P2SH). + +**Key compromise.** A stolen share owner key allows redirecting that share's +future rewards and triggering a penalized unilateral dissolution — never +principal theft. Because keys cannot be rotated, victim and thief hold +identical capabilities; the honest holder always retains the exit right. + +**Key loss.** Principal is never endangered: any other participant's unilateral +dissolution refunds the key-less participant in full, and outside the early +period this costs the rescuer only the transaction fee. A key-less participant +cannot force exit, one lost key permanently disables the unanimous and +whole-masternode update paths, and losing all owner keys freezes the collateral +forever. Standby dissolutions (see above) reduce all of these to "keep your +standby with your refund-key backup". + +**Hostage and griefing dynamics.** A participant who refuses to sign +whole-masternode updates can degrade the node (for example, blocking operator +key replacement), and an honest leaver both pays the early penalty and pays a +pro-rata slice of it to the obstructive participant. This is bounded: the +initially configured operator key keeps working indefinitely, exit after the +early period is penalty-free, and the early penalty is a term every participant +consented to at formation. Wallets must present penalty terms prominently, and +must warn that a zero `earlyPenalty` permits cheap early griefing. + +**Transaction-identifier malleability.** The covenant input's empty-`scriptSig` +rule and the low-S requirement on all payload signatures pin every free byte of +a ProDisTx; inputs, sequences, outputs, and lock time are covered by the signed +digest. Without these rules, third parties could mutate a pending dissolution's +txid, breaking child-pays-for-parent fee bumps. + +**Forbidden destinations.** The list in +[Forbidden Template Destinations](#forbidden-template-destinations) is +consensus-critical: missing any entry creates either permanently frozen +collateral or an unconstructable coinbase (a consensus deadlock) via a crafted +script. + +## Privacy Considerations + +Shared ownership is consensus-visible by construction: + +* the share table — amounts, owner key IDs, refund scripts, reward scripts, and + penalty terms — is public on-chain forever; +* every payout block creates up to 8 owner outputs that persistently cluster + the co-owners' scripts with each other and with the masternode; +* the immutable refund script is an address pre-commitment made years before + the exit that eventually pays it. + +Wallets should use fresh keys and destinations for every formation, never reuse +a refund destination across formations or with other on-chain activity, and may +rotate reward scripts freely. Participants who want unlinkability between their +share and their other funds must break the link before funding (for example, +by mixing the funding inputs); note that refunds return as large, +non-denominated outputs. + +## Copyright + +Copyright (c) 2026 Dash Core Group, Inc. [Licensed under the MIT License](https://opensource.org/licenses/MIT) diff --git a/project-words.txt b/project-words.txt index 85f03cf8..470e1678 100644 --- a/project-words.txt +++ b/project-words.txt @@ -10,6 +10,7 @@ CBLS CBLSIES cbitset CBOR +ANYONECANPAY chainwork CHECKDATASIG CHECKDATASIGVERIFY @@ -31,6 +32,7 @@ dptp dpts DSPC DVRF +DSHC ECDH ecdh evonode @@ -55,6 +57,8 @@ Keybase libsecp LLMQ Masternode +malleate +malleation masternodes mainnet MERKLEBLOCK @@ -70,6 +74,8 @@ preorder Preorder preordered prevout +prevouts +ProUpServTx protx pseudorandomness QDATA @@ -92,6 +98,8 @@ RIPEMD sendheaders SEPA signmessage +sighash +SIGHASH Skiplist Spork subtx @@ -104,6 +112,9 @@ topups tprv tpub unretrievable +unconstructable +unlinkability +trustlessly UTXO varint vvec @@ -162,4 +173,4 @@ Udjin Udjinm Virgile Westrich -Wray \ No newline at end of file +Wray From e7eba87e9f9fb02641608b938c638266e1072809 Mon Sep 17 00:00:00 2001 From: Pasta Date: Thu, 9 Jul 2026 19:18:58 -0500 Subject: [PATCH 02/12] docs: align shares DIP with the reference implementation - 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 --- dip-pasta-shared-masternode-collateral.md | 57 +++++++++++++++++++---- project-words.txt | 1 + 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index fa987e8c..03ff430f 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -252,6 +252,11 @@ There is no mode field: **the signature count defines the mode**. Exactly one signature — which must be by `shares[actorIndex].ownerKeyID` — is a unilateral dissolution. Exactly `sharesCount` signatures — one per share, in share order — is a unanimous dissolution. Any other count, order, or signer set is invalid. +The signature count is committed into `SharedDisHash` (below), so the mode is +bound into every signature: a third party cannot reinterpret a penalty-free +unanimous dissolution as a unilateral one (or vice versa) by dropping or adding +signatures, which would otherwise leave the inputs and outputs unchanged but +alter the transaction identifier. A ProDisTx must have exactly one input: the masternode's collateral outpoint, with an empty `scriptSig`. It must have no outputs other than those required @@ -263,11 +268,13 @@ below. "DashSharedMNDissolve" || payload version || tx version || tx type || tx nLockTime || all input prevouts || all input sequences || all outputs || -proTxHash || actorIndex +proTxHash || actorIndex || sigCount ``` The digest commits to the transaction's actual input and outputs directly; the -payload deliberately carries no `inputsHash`/`outputsHash` copies. +payload deliberately carries no `inputsHash`/`outputsHash` copies. It also +commits to `sigCount`, which selects the mode, so the unilateral/unanimous +distinction cannot be malleated after signing. #### Required penalty @@ -326,9 +333,10 @@ Dash relay supports neither BIP125 replacement nor package relay, so a ProDisTx must embed a sufficient fee at signing time; child-pays-for-parent on the actor output can only raise the mining priority of a ProDisTx that already meets the minimum relay fee. A flat fee of roughly 100000 duffs — about one millionth of -the minimum share — provides large fee-market headroom. Relay policy may allow -a ProDisTx to replace a pending ProDisTx spending the same collateral when it -pays a strictly higher fee. +the minimum share — provides large fee-market headroom. Because Dash relay has +no replacement mechanism, the first ProDisTx to reach the mempool for a given +collateral wins; a competing ProDisTx conflicts on the collateral input and is +rejected until the first is mined or dropped. Because validity is monotone and share owner keys are immutable, a signed ProDisTx never goes stale. Wallets should have each participant sign a @@ -380,7 +388,6 @@ Payload: | proTxHash | uint256 | 32 | The ProRegTx hash of the shared masternode. | | pubKeyOperator | BLSPubKey | 48 | New operator public key. | | keyIdVoting | CKeyID | 20 | New voting key ID. | -| operatorReward | uint16_t | 2 | New operator reward in basis points (0 to 10000). | | inputsHash | uint256 | 32 | Hash of all transaction inputs, as in DIP-0003 update payloads. | | sigCount | uint8_t | 1 | Must equal `sharesCount`. | | sigs | signature[sigCount] | 65 * sigCount | Canonical signatures over the payload hash (with this field empty), one per share, in share order. | @@ -391,6 +398,10 @@ Operator-key change semantics (service reset) match ProUpRegTx in DIP-0003. A ProUpSharedRegTx referencing a non-shared masternode is invalid, and a plain ProUpRegTx referencing a shared masternode is invalid. +The operator reward is fixed at registration and is not updatable, matching the +DIP-0003 ProUpRegTx model (where `operatorReward` is likewise immutable after +registration); it is therefore not carried in this payload. + Operator-authorized updates are unchanged: the operator continues to manage service fields via ProUpServTx (including the operator payout script) and may revoke via ProUpRevTx. The operator can stop service but cannot spend @@ -448,6 +459,15 @@ implementations must enforce, at both mempool acceptance and block connection: 4. Masternode removal for a shared masternode occurs only through a validated ProDisTx. Collateral-spend removal semantics for normal masternodes are unchanged (normal collateral never carries the template). +5. A validated ProDisTx removes its masternode in the same collateral-spend + phase of deterministic-list construction as an ordinary collateral spend — + that is, after all of the block's provider transactions (registrations and + updates) have been applied. The dissolution is validated at its position in + the transaction order (so it sees the masternode and any earlier same-block + registration), but the removal itself is deferred to that phase. A shared + masternode update (ProUpShareTx or ProUpSharedRegTx) and a dissolution of the + same masternode may therefore appear together in one block in either order: + the update always applies before the removal takes effect. Mempool implementations should additionally evict pending ProUpShareTx and ProUpSharedRegTx transactions for a masternode when its ProDisTx confirms. @@ -473,10 +493,27 @@ wherever a user-supplied script later becomes a consensus-mandated output: ### Special Transaction Filtering -Special transaction filters and bloom filters must include every share -`refundScript`, `rewardScript`, and `ownerKeyID` used in a shared registration, -ProDisTx, ProUpShareTx, or ProUpSharedRegTx, in the same manner that DIP-0026 -requires for payout scripts. +A shared **registration** carries the full share table, so special transaction +filters and bloom filters must match it on every share `refundScript`, +`rewardScript`, and `ownerKeyID`, in the same manner that DIP-0026 requires for +payout scripts. + +The lifecycle transactions do not carry the share table — only the `proTxHash` +identifying the masternode — so, consistent with the DIP-0003 treatment of +ProUpRegTx and ProUpRevTx, they are matched by `proTxHash` (plus the new +`rewardScript` for ProUpShareTx). A light client learns a masternode's share +scripts and owner keys from the registration it matched, then follows that +masternode's lifecycle by its `proTxHash`. Two properties make this sufficient +in practice: the refund and reward payments produced by a ProDisTx are ordinary +transaction outputs, matched by script in the base filter, so a client watching +its refund or reward script sees its funds without any special-transaction +logic; and a BIP37 bloom filter with `BLOOM_UPDATE_ALL` inserts the `proTxHash` +automatically when it matches the registration, so an owner-key watcher receives +subsequent lifecycle transactions without further configuration. + +Requiring stateless filters to match the lifecycle transactions on the share +`ownerKeyID`s is not possible, because those transactions do not contain the +keys and filter construction performs no masternode-list lookup. ## Deployment and Compatibility diff --git a/project-words.txt b/project-words.txt index 470e1678..b6312e7d 100644 --- a/project-words.txt +++ b/project-words.txt @@ -58,6 +58,7 @@ libsecp LLMQ Masternode malleate +malleated malleation masternodes mainnet From b360f4ccaf6eb8524d9429c4f029f3dee2f56bbd Mon Sep 17 00:00:00 2001 From: pasta Date: Sat, 1 Aug 2026 13:34:27 -0500 Subject: [PATCH 03/12] docs: track v3/v4 provider version merge and same-block dissolution rule 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 --- dip-pasta-shared-masternode-collateral.md | 64 +++++++++++++---------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index 03ff430f..42e5c53c 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -100,13 +100,17 @@ This DIP is therefore a strict superset of DIP-0026: ### Provider Transaction Version -This DIP extends the version 4 (`MultiPayout`) ProRegTx payload defined in -DIP-0026, prior to its release in any Dash Core version. A version 4 ProRegTx -with a non-zero `sharesCount` is a **shared registration**. A version 4 -ProRegTx with `sharesCount = 0` behaves exactly as specified in DIP-0026. - -Because version 4 has not been released, the additional fields below change the -serialization of all version 4 ProRegTx payloads (a non-shared payload still +This DIP extends the version 3 (extended addresses) ProRegTx payload, which +carries the DIP-0026 `payouts` field, prior to its release in any Dash Core +version. DIP-0026's multi-party payouts were originally specified as a separate +version 4 (`MultiPayout`); because both versions are introduced by the same +deployment, the reference implementation folded version 4 into version 3, and +this DIP follows that layout. A version 3 ProRegTx with a non-zero +`sharesCount` is a **shared registration**. A version 3 ProRegTx with +`sharesCount = 0` behaves exactly as specified in DIP-0026. + +Because version 3 has not been released, the additional fields below change the +serialization of all version 3 ProRegTx payloads (a non-shared payload still serializes `sharesCount = 0`, an empty share list, and zeroed penalty fields). This DIP and DIP-0026 must therefore deploy in the same release. @@ -154,7 +158,7 @@ Each share has the following structure: | rewardScript | Script | Variable | Reward payout script (P2PKH/P2SH). Zero length means "use refundScript". | | ownerKeyID | CKeyID | 20 | Immutable share owner key ID. | -For shared registrations, the following fields are appended to the version 4 +For shared registrations, the following fields are appended to the version 3 ProRegTx payload immediately after the `payouts` field defined in DIP-0026: | Field | Type | Size | Description | @@ -173,7 +177,7 @@ affected transaction identifiers non-malleable by third parties. ### Registering a Shared Masternode -A shared registration (version 4 ProRegTx, `sharesCount > 0`) is valid only if +A shared registration (version 3 ProRegTx, `sharesCount > 0`) is valid only if all of the following hold, in addition to DIP-0003 rules not explicitly replaced here: @@ -186,7 +190,7 @@ replaced here: equals the required collateral and whose `scriptPubKey` is exactly the template. 4. The DIP-0026 `payouts` list is empty (owner rewards derive from the share - table) and the legacy `scriptPayout` is absent per version 4. + table) and the legacy `scriptPayout` is absent per version 3. 5. `keyIdOwner` is all zeros. The share owner keys replace it. 6. `sharesCount` is between 2 and 8 inclusive. 7. Every share `amount` is at least 100 DASH, and the amounts sum exactly to @@ -453,21 +457,22 @@ implementations must enforce, at both mempool acceptance and block connection: input; the masternode list is consulted only inside ProDisTx validation. 2. **Creation check:** any transaction output whose script equals the template, outside the collateral slot of a valid shared registration, is invalid. -3. Block-level validation evaluates ProDisTx against the deterministic - masternode list as of the previous block plus earlier transactions in the - same block (a masternode may be registered and dissolved within one block). +3. A ProDisTx is validated against the deterministic masternode list as of + the previous block, in blocks exactly as in the mempool. Registering and + dissolving the same masternode within one block is invalid. Everything a + dissolution is validated against (refund scripts, amounts, registration + height) is immutable after registration, so this single validation context + is complete and block validation reuses the mempool path. 4. Masternode removal for a shared masternode occurs only through a validated ProDisTx. Collateral-spend removal semantics for normal masternodes are unchanged (normal collateral never carries the template). 5. A validated ProDisTx removes its masternode in the same collateral-spend phase of deterministic-list construction as an ordinary collateral spend — - that is, after all of the block's provider transactions (registrations and - updates) have been applied. The dissolution is validated at its position in - the transaction order (so it sees the masternode and any earlier same-block - registration), but the removal itself is deferred to that phase. A shared - masternode update (ProUpShareTx or ProUpSharedRegTx) and a dissolution of the - same masternode may therefore appear together in one block in either order: - the update always applies before the removal takes effect. + that is, after all of the block's provider transactions have been applied. + A shared masternode update (ProUpShareTx or ProUpSharedRegTx) and a + dissolution of the same masternode may therefore appear together in one + block in either order: the update always applies before the removal takes + effect. Mempool implementations should additionally evict pending ProUpShareTx and ProUpSharedRegTx transactions for a masternode when its ProDisTx confirms. @@ -530,8 +535,8 @@ After activation: * All rules in this DIP apply. * Template outputs created before activation are permanently unspendable. -* Existing masternodes (legacy and DIP-0026 version 4 non-shared) are - unaffected. Normal masternode collateral-spend semantics are unchanged. +* Existing masternodes (legacy and version 3 non-shared) are unaffected. + Normal masternode collateral-spend semantics are unchanged. Shared registration is restricted to Regular masternodes in this version. Extending shared ownership to Evo masternodes requires a Platform-side @@ -606,12 +611,13 @@ regime because the minimum-based rules degenerate cleanly to "each participant receives at least their share" when the required penalty is zero. -### Why this extends provider transaction version 4 +### Why this extends provider transaction version 3 -DIP-0026's version 4 has not shipped, so folding shared collateral into the -same version avoids an additional version solely for field layering, at the -cost of a wire-format change to an unreleased payload version. The two DIPs -deploy together in v24. +Version 3 (extended addresses, which carries DIP-0026's payouts after the +version 4 fold) has not shipped, so folding shared collateral into the same +version avoids an additional version solely for field layering, at the cost of +a wire-format change to an unreleased payload version. The two DIPs deploy +together in v24. ### Bounds @@ -653,7 +659,9 @@ Implementations should include tests for at least the following: non-empty `scriptSig` on the collateral input; wrong actor signature; signature count other than 1 or `sharesCount`; unanimous signatures out of share order. -8. Registration and dissolution of the same masternode within one block. +8. Invalid: registration and dissolution of the same masternode within one + block (a dissolution is valid only once its registration is contained in a + prior block). 9. A pending dissolution remains valid across confirming ProUpShareTx and ProUpSharedRegTx transactions; a standby dissolution signed at registration broadcasts successfully after the early-period boundary. From c19da1d987ccf6e241ac77412668b6cb512edf1f Mon Sep 17 00:00:00 2001 From: pasta Date: Sat, 1 Aug 2026 14:39:23 -0500 Subject: [PATCH 04/12] docs: commit input sequences into the registration consent digest 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 --- dip-pasta-shared-masternode-collateral.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index 42e5c53c..14b14272 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -214,7 +214,7 @@ replaced here: ```text "DashSharedMNReg" || payload version || tx version || tx type || tx nLockTime || -inputsHash || outputsHash || +inputsHash || all input sequences || outputsHash || type || mode || netInfo and Platform fields || keyIdVoting || pubKeyOperator || operatorReward || shares || earlyPeriodBlocks || earlyPenalty @@ -222,17 +222,21 @@ shares || earlyPeriodBlocks || earlyPenalty where `inputsHash` and `outputsHash` are as defined in DIP-0003, and `shares` is the serialized share table. Consent therefore binds every participant to the -exact funding inputs, all outputs (including the collateral output and every -change output), the full share table, the penalty terms, and the registrar -configuration. Consent never relies on the sighash modes of funding-input -signatures, because Dash transaction signatures permit `SIGHASH_NONE`, -`SIGHASH_SINGLE`, and `SIGHASH_ANYONECANPAY`. +exact funding inputs (prevouts and sequences), all outputs (including the +collateral output and every change output), the full share table, the penalty +terms, and the registrar configuration. Consent never relies on the sighash +modes of funding-input signatures, because Dash transaction signatures permit +`SIGHASH_NONE`, `SIGHASH_SINGLE`, and `SIGHASH_ANYONECANPAY`. Covering the +input sequences matters: BIP68 gives them consensus meaning on version 2 and +later transactions, so an uncovered sequence rewrite between consent signing +and funding-input signing could impose a months-long relative timelock on a +fully consented registration. Registration is atomic: if any funding input is double-spent or any participant withholds a `joinSig`, no shared masternode is created and no participant's funds move. Co-signer transaction-identifier malleability is harmless: nothing is pre-signed against the registration txid, and the consent digest binds to -prevouts and outputs, which malleation cannot change. +prevouts, sequences, and outputs, which malleation cannot change. ### Dissolving a Shared Masternode (ProDisTx) From 32fd7d2692a1e9ab5ee901402a911493ce526253 Mon Sep 17 00:00:00 2001 From: pasta Date: Wed, 12 Aug 2026 16:29:43 -0500 Subject: [PATCH 05/12] docs: address decentralized shares review feedback --- dip-pasta-shared-masternode-collateral.md | 249 ++++++++++++---------- project-words.txt | 2 - 2 files changed, 139 insertions(+), 112 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index 14b14272..a55bb44d 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -58,20 +58,21 @@ Considerations section notes that "a share arrangement that requires immutable payout rights must use additional contractual, wallet, or protocol mechanisms outside the scope of this DIP." This DIP is that protocol mechanism. -Without it, shared masternode ownership requires trusting a collateral holder: -whoever controls the collateral key can spend the collateral, destroy the -masternode, and abscond with the other participants' principal. Pre-signed -multi-signature refund transactions cannot fix this on Dash: without SegWit or -Taproot, a co-signer can malleate the funding transaction identifier and -invalidate every pre-signed child. The only robust construction available is a -consensus-enforced covenant: the protocol itself records each participant's -contribution, refund script, reward script, and owner key, and enforces -refunds, rewards, and update consent directly. +Without it, a single collateral holder can spend the collateral, destroy the +masternode, and take the other participants' principal. A P2SH multi-signature +collateral can prevent unilateral theft, but it cannot guarantee unilateral +exit. A dissolution signed before the funding transaction confirms is exposed +to co-signer transaction-identifier malleability on Dash, while signing it +after confirmation still depends on enough participants remaining available +and cooperative. It also does not protect recurring payout rights. This DIP +instead records each participant's contribution, refund script, reward script, +and owner key in consensus and directly enforces refunds, rewards, and update +consent. This DIP is therefore a strict superset of DIP-0026: -* DIP-0026: protocol-enforced recurring reward split; -* this DIP: protocol-enforced funding, reward split, update consent, and +* DIP-0026: Protocol-enforced recurring reward split; +* This DIP: Protocol-enforced funding, reward split, update consent, and trustless exit. ## Prior Work @@ -94,19 +95,16 @@ This DIP is therefore a strict superset of DIP-0026: | Share owner key | The ECDSA key (as a key ID) that authorizes a participant's consensus actions. Immutable. | | Early period | The first `earlyPeriodBlocks` blocks after registration, during which unilateral exit is penalized. | | Dissolution | The consensus-enforced spend of shared collateral that refunds all participants and removes the masternode. | +| ProDisTx | The special transaction defined by this DIP that performs a dissolution. | | Actor | The participant designated by `actorIndex` in a dissolution; pays the penalty (if any) and the transaction fee. | | Unilateral dissolution | A dissolution authorized by exactly one participant. | | Unanimous dissolution | A dissolution authorized by every participant. | ### Provider Transaction Version -This DIP extends the version 3 (extended addresses) ProRegTx payload, which -carries the DIP-0026 `payouts` field, prior to its release in any Dash Core -version. DIP-0026's multi-party payouts were originally specified as a separate -version 4 (`MultiPayout`); because both versions are introduced by the same -deployment, the reference implementation folded version 4 into version 3, and -this DIP follows that layout. A version 3 ProRegTx with a non-zero -`sharesCount` is a **shared registration**. A version 3 ProRegTx with +This DIP extends the unreleased version 3 (extended addresses) ProRegTx payload, +which carries the DIP-0026 `payouts` field. A version 3 ProRegTx with a non-zero +`sharesCount` is a shared registration. A version 3 ProRegTx with `sharesCount = 0` behaves exactly as specified in DIP-0026. Because version 3 has not been released, the additional fields below change the @@ -117,25 +115,25 @@ This DIP and DIP-0026 must therefore deploy in the same release. ### Shared Collateral Script The collateral output of a shared registration must use exactly the following -7-byte script (the **template**): +7-byte script (the template): ```text 0x04 "DSHC" OP_DROP OP_TRUE hex: 04445348437551 ``` -Template matching is always **exact-script** comparison against these 7 bytes, +Template matching is always exact-script comparison against these 7 bytes, never prefix matching. The template contains no keys and is spendable with an empty `scriptSig` at the script layer. All protection comes from the following consensus rules, active from deployment activation: -* **Creation:** an output whose `scriptPubKey` equals the template is valid +* Creation: An output whose `scriptPubKey` equals the template is valid only as the collateral output of a valid shared registration. Any other creation — in a normal transaction, a coinbase, another special transaction, or a non-collateral output of the registration itself — is invalid. -* **Spending:** a transaction spending an output whose `scriptPubKey` equals +* Spending: A transaction spending an output whose `scriptPubKey` equals the template is valid only if it is a valid ProDisTx whose referenced masternode owns that outpoint. @@ -169,11 +167,14 @@ ProRegTx payload immediately after the `payouts` field defined in DIP-0026: | earlyPeriodBlocks | uint32_t | 4 | Length of the early period in blocks. | | earlyPenalty | int64_t | 8 | Penalty in duffs for unilateral dissolution during the early period. | -All signatures introduced by this DIP (`joinSigs` and ProDisTx signatures) are -65-byte compact recoverable ECDSA signatures with **enforced low-S** (the -signature scheme used for owner-key payload signatures in DIP-0003). High-S -signatures are invalid. This, combined with the digest rules below, makes the -affected transaction identifiers non-malleable by third parties. +These fields are serialized in the table order shown above. + +All signatures introduced by this DIP, including `joinSigs`, ProDisTx, +ProUpShareTx, and ProUpSharedRegTx signatures, use the owner-key signature +scheme from DIP-0003 and must be canonical. Compact signatures are 65-byte +recoverable ECDSA signatures, and all signatures must use low-S form. High-S +signatures are invalid. This, combined with the digest rules below, prevents +third-party malleation of the affected transaction identifiers. ### Registering a Shared Masternode @@ -201,9 +202,10 @@ replaced here: owner key registered by any other masternode (see [Masternode List State](#masternode-list-state)); duplicate `refundScript`s within the share table are invalid; duplicate `rewardScript`s are allowed. -10. No `refundScript` or `rewardScript` pays P2PK or P2PKH to any share - `ownerKeyID` in the table or to `keyIdVoting` (extends the DIP-0026 - payee-reuse rules), and none equals the template +10. Every `refundScript` and non-empty `rewardScript` is P2PKH or P2SH. A P2PKH + destination must not equal any share `ownerKeyID` in the table or + `keyIdVoting` (extending the DIP-0026 payee-reuse rules), and no script may + equal the template (see [Forbidden Template Destinations](#forbidden-template-destinations)). 11. The external-collateral `payloadSig` is empty, and each entry of `joinSigs` is a valid canonical signature by the corresponding share's `ownerKeyID` @@ -229,8 +231,8 @@ modes of funding-input signatures, because Dash transaction signatures permit `SIGHASH_NONE`, `SIGHASH_SINGLE`, and `SIGHASH_ANYONECANPAY`. Covering the input sequences matters: BIP68 gives them consensus meaning on version 2 and later transactions, so an uncovered sequence rewrite between consent signing -and funding-input signing could impose a months-long relative timelock on a -fully consented registration. +and funding-input signing could lock a fully consented registration for several +months. Registration is atomic: if any funding input is double-spent or any participant withholds a `joinSig`, no shared masternode is created and no participant's @@ -256,7 +258,7 @@ Payload: | sigCount | uint8_t | 1 | Number of signatures: exactly 1 (unilateral) or exactly `sharesCount` (unanimous). | | sigs | signature[sigCount] | 65 * sigCount | Canonical signatures over `SharedDisHash`. | -There is no mode field: **the signature count defines the mode**. Exactly one +There is no mode field: the signature count defines the mode. Exactly one signature — which must be by `shares[actorIndex].ownerKeyID` — is a unilateral dissolution. Exactly `sharesCount` signatures — one per share, in share order — is a unanimous dissolution. Any other count, order, or signer set is invalid. @@ -298,11 +300,13 @@ non-increasing in `spendHeight`. #### Output rules -All output rules are **minimum-based**: paying more penalty than required is +All output rules are minimum-based: paying more penalty than required is always valid. Because `requiredPenalty` is non-increasing, a ProDisTx that is valid at height `h` is valid at every height `h' >= h`: no dissolution is -silently invalidated by chain progress. The converse is intentionally not true: -a unilateral ProDisTx paying no penalty is invalid until the early period ends. +silently invalidated by chain progress. A unilateral ProDisTx is invalid +whenever it pays less than `requiredPenalty`. A zero-penalty standby becomes +valid when the required penalty reaches zero, or immediately when +`earlyPenalty` is zero. Let `a = actorIndex`, `W` = the sum of non-actor share amounts, and `P = requiredPenalty`. A ProDisTx is valid only if: @@ -316,6 +320,9 @@ Let `a = actorIndex`, `W` = the sum of non-actor share amounts, and `bonus[i] >= floor(P * shares[i].amount / W)`. 4. The sum of all `bonus[i]` is at least `P`. +The multiplication in rule 3 must use an intermediate at least 128 bits wide; +a signed 64-bit intermediate can overflow for valid amounts. + Everything not paid to these outputs is the transaction fee, which by value conservation can only come from the actor's share; the actor output is therefore implicitly capped at `shares[a].amount - P`. @@ -348,14 +355,13 @@ rejected until the first is mined or dropped. Because validity is monotone and share owner keys are immutable, a signed ProDisTx never goes stale. Wallets should have each participant sign a -**standby dissolution** immediately after registration confirms — unilateral, +standby dissolution immediately after registration confirms — unilateral, paying `earlyPenalty` (valid immediately and forever) or no penalty (valid from the end of the early period, forever) — and store it with the participant's refund-key backup, separately from the owner key. If the owner key is later lost, broadcasting the standby recovers the participant's principal without any -other party's cooperation. A leaked standby is griefing-only: every output is -covenant-fixed, so its holder can trigger the exit (costing the signer the -penalty) but cannot redirect any value. +other party's cooperation. A leaked standby can force an early exit, but every +output is fixed by the covenant, so its holder cannot redirect any value. ### Updating a Share (ProUpShareTx) @@ -373,7 +379,8 @@ Payload: | rewardScriptSize | compactSize uint | 1-9 | Size of the new reward script. | | rewardScript | Script | Variable | New reward script (P2PKH/P2SH), or zero length for "use refundScript". | | inputsHash | uint256 | 32 | Hash of all transaction inputs, as in DIP-0003 update payloads. | -| payloadSig | signature | 65 | Canonical signature by `shares[shareIndex].ownerKeyID` over the payload hash (with this field empty). | +| payloadSigSize | compactSize uint | 1-9 | Size of the signature. Must encode 65. | +| payloadSig | unsigned char[] | Variable | Canonical signature by `shares[shareIndex].ownerKeyID` over the payload hash (with this field empty). | A ProUpShareTx updates exactly one share's `rewardScript` and nothing else. Share `amount`, `refundScript`, `ownerKeyID`, the participant count, the @@ -401,7 +408,7 @@ Payload: | sigs | signature[sigCount] | 65 * sigCount | Canonical signatures over the payload hash (with this field empty), one per share, in share order. | A ProUpSharedRegTx updates the whole-masternode registrar fields and requires -one valid signature from **every** current share owner key, in share order. +one valid signature from every current share owner key, in share order. Operator-key change semantics (service reset) match ProUpRegTx in DIP-0003. A ProUpSharedRegTx referencing a non-shared masternode is invalid, and a plain ProUpRegTx referencing a shared masternode is invalid. @@ -428,39 +435,48 @@ directions: a share `ownerKeyID` must not equal any other masternode's registered owner key (shared or `keyIdOwner`), and a normal registration must not reuse any active share owner key. +Proof-of-Service rules are unchanged for shared masternodes. In particular, a +valid ProUpServTx revives a banned shared masternode once its operator and +voting keys are set; the intentionally null legacy `keyIdOwner` does not prevent +revival. + Share data is excluded from the simplified masternode list entry hash (`CSimplifiedMNListEntry`), matching DIP-0026's treatment of payout data. -Extended JSON forms may expose shares for diagnostics; this is not a -light-client commitment. Full nodes validate shared rewards and dissolutions -from deterministic masternode state reconstructed from blocks. +RPCs that expose full deterministic state, including `protx info`, should return +the share table, `earlyPeriodBlocks`, and `earlyPenalty`. Extended simplified +masternode list JSON should expose the share table so clients can observe share +updates. None of these JSON fields is a light-client commitment. Full nodes +validate shared rewards and dissolutions from deterministic masternode state +reconstructed from blocks. ### Masternode Reward Payments Reward payment construction for a shared masternode follows the DIP-0026 procedure exactly, with the owner payout entries derived from the share table: -* the entry ordering is the share-table order; -* the entry weight is the share `amount` (instead of basis points), so entry +* The entry ordering is the share-table order; +* The entry weight is the share `amount` (instead of basis points), so entry `i` (except the last) receives `floor(ownerReward * amount[i] / collateral)` and the last entry receives the remainder; -* the entry destination is the share's `rewardScript` if non-empty, otherwise +* The entry destination is the share's `rewardScript` if non-empty, otherwise its `refundScript`; -* zero-amount outputs are omitted, and operator reward handling is unchanged. +* Zero-amount outputs are omitted, and operator reward handling is unchanged. This reuses DIP-0026's single rounding convention (sequential floor, remainder -to the last entry) rather than introducing a second one. +to the last entry) rather than introducing a second one. Implementations must +use an intermediate at least 128 bits wide for the amount multiplication. ### Collateral Spend Enforcement Template rules cannot live only inside provider-transaction checks. Consensus implementations must enforce, at both mempool acceptance and block connection: -1. **Spend check:** for every transaction, if any input's previous output - script equals the template, the transaction must be a valid ProDisTx - spending that outpoint. This is a constant exact-script comparison per - input; the masternode list is consulted only inside ProDisTx validation. -2. **Creation check:** any transaction output whose script equals the template, - outside the collateral slot of a valid shared registration, is invalid. +1. For every transaction, if any input's previous output script equals the + template, the transaction must be a valid ProDisTx spending that outpoint. + This is a constant exact-script comparison per input; the masternode list is + consulted only inside ProDisTx validation. +2. Any transaction output whose script equals the template, outside the + collateral slot of a valid shared registration, is invalid. 3. A ProDisTx is validated against the deterministic masternode list as of the previous block, in blocks exactly as in the mempool. Registering and dissolving the same masternode within one block is invalid. Everything a @@ -483,34 +499,36 @@ ProUpSharedRegTx transactions for a masternode when its ProDisTx confirms. ### Forbidden Template Destinations -Because template outputs may only be created inside shared registrations, any -consensus-mandated output paying the template would be unconstructable and -could deadlock consensus. Validation must therefore reject the template script -wherever a user-supplied script later becomes a consensus-mandated output: +Because template outputs may only be created inside shared registrations, a +consensus-mandated output paying the template would be impossible to construct +and could deadlock consensus. Validation must therefore reject the template +script wherever a user-supplied script later becomes a consensus-mandated +output: -* share `refundScript` and `rewardScript` values (registration and +* Share `refundScript` and `rewardScript` values (registration and ProUpShareTx) — a template refund script would make every dissolution invalid, freezing the collateral forever; -* every provider-transaction payout script for every masternode type and +* Every provider-transaction payout script for every masternode type and payload version, including DIP-0026 payout entries and the operator payout - script — a template payout would make the coinbase unconstructable when that - masternode is paid; -* governance proposal and trigger payment scripts (validated after + script — a template payout would make the coinbase impossible to construct + when that masternode is paid; +* Governance proposal and trigger payment scripts (validated after activation) — a template payee would invalidate the superblock carrying it; -* asset-unlock (credit withdrawal) destinations — a template destination would +* Asset-unlock (credit withdrawal) destinations — a template destination would make the withdrawal transaction invalid. ### Special Transaction Filtering -A shared **registration** carries the full share table, so special transaction +A shared registration carries the full share table, so special transaction filters and bloom filters must match it on every share `refundScript`, `rewardScript`, and `ownerKeyID`, in the same manner that DIP-0026 requires for payout scripts. The lifecycle transactions do not carry the share table — only the `proTxHash` identifying the masternode — so, consistent with the DIP-0003 treatment of -ProUpRegTx and ProUpRevTx, they are matched by `proTxHash` (plus the new -`rewardScript` for ProUpShareTx). A light client learns a masternode's share +ProUpRegTx and ProUpRevTx, they are matched by `proTxHash`. ProUpShareTx is also +matched by its new `rewardScript`, and ProUpSharedRegTx by its new +`keyIdVoting`. A light client learns a masternode's share scripts and owner keys from the registration it matched, then follows that masternode's lifecycle by its `proTxHash`. Two properties make this sufficient in practice: the refund and reward payments produced by a ProDisTx are ordinary @@ -528,6 +546,8 @@ keys and filter construction performs no masternode-list lookup. This DIP deploys with Dash Core's v24 network upgrade (`DEPLOYMENT_V24`, an Enhanced Hard Fork per [DIP-0023](dip-0023.md)), together with DIP-0026. +Contextual validation must recognize special transaction types 10 through 12 +so they reach their type-specific checks; those checks enforce the v24 gate. Before activation: @@ -552,12 +572,14 @@ would reintroduce the trust this DIP removes. ### Why a consensus covenant instead of multi-signature scripts -Dash lacks SegWit and Taproot, so transaction identifiers are malleable by -co-signers and pre-signed refund transactions are unsafe. A P2SH multi-sig -collateral script would additionally freeze funds if signers disappear and -cannot express per-participant refund amounts. Recording ownership in the -provider payload and enforcing spends in consensus — the same trust model as -DIP-0003's deterministic masternode lists — avoids both problems. The price is +P2SH multi-signature collateral can prevent unilateral theft, but its threshold +also makes exit depend on continued signer cooperation. A pre-signed +dissolution can encode the refund amounts, but signing it before funding +confirmation is unsafe on Dash because a co-signer can change the funding +transaction identifier; signing after confirmation preserves the cooperation +requirement. Recording ownership and refund amounts in the provider payload and +enforcing spends in consensus — the same trust model as DIP-0003's deterministic +masternode lists — provides each participant an independent exit. The price is that the collateral output is anyone-can-spend at the script layer on any chain that does not enforce these rules; see [Security Considerations](#security-considerations). @@ -598,7 +620,7 @@ sign years in advance. Redistribution compensates remaining participants for losing their node and payment-queue position. Burning would punish the innocent majority to deny a -hypothetical griefer a small bonus. A penalty waiver triggered by sustained +malicious participant a small bonus. A penalty waiver triggered by sustained PoSe ban was considered and rejected: the operator controls PoSe status, and in practice a participant is often the operator, so a waiver lets a participant deliberately get the node banned and exit penalty-free during the early period, @@ -617,11 +639,10 @@ zero. ### Why this extends provider transaction version 3 -Version 3 (extended addresses, which carries DIP-0026's payouts after the -version 4 fold) has not shipped, so folding shared collateral into the same -version avoids an additional version solely for field layering, at the cost of -a wire-format change to an unreleased payload version. The two DIPs deploy -together in v24. +Version 3 (extended addresses, which carries DIP-0026's payouts) has not shipped, +so using the same version avoids an additional payload version solely for field +layering, at the cost of a wire-format change to an unreleased payload. The two +DIPs deploy together in v24. ### Bounds @@ -636,7 +657,9 @@ own fee. Implementations should include tests for at least the following: -1. A valid shared registration with 2 and with 8 participants. +1. A valid shared registration with 2 and with 8 participants; exact + serialization round-trips for shared payloads and for a non-shared version 3 + payload with an empty share table and zeroed penalty fields. 2. Invalid: shared Evo registration; external shared collateral; share sum not equal to the collateral; share below 100 DASH; `earlyPenalty >= min(share)`; early period above the cap; non-empty DIP-0026 `payouts` with shares; @@ -645,8 +668,9 @@ Implementations should include tests for at least the following: (both directions with normal masternodes); duplicate refund scripts; refund or reward script paying P2PKH to any share owner key or the voting key; refund or reward script equal to the template. -4. Invalid: missing or non-canonical (high-S) `joinSig`; consent digest - mismatch after changing any covered field. +4. Invalid: missing or non-canonical (high-S) signature in each signature-bearing + payload introduced by this DIP; consent digest mismatch after changing any + covered field. 5. Invalid: any normal transaction spending a template output; post-activation template-output creation in a normal transaction, a coinbase, or a non-collateral output of a shared registration. A near-miss script (template @@ -675,16 +699,20 @@ Implementations should include tests for at least the following: share order; plain ProUpRegTx is invalid for a shared masternode. 11. Reward split across mined blocks: conservation, remainder to the last entry, zero-output omission, operator reward interaction, reward script - fallback to refund script. + fallback to refund script, and amounts large enough to overflow a 64-bit + multiplication. 12. Reorg across registration, across the early-period boundary, and across update-then-dissolution ordering; restart from a deterministic masternode list snapshot before and after shared state changes. -13. Filter matching for every share refund script, reward script, and owner - key. +13. Filter matching for every share refund script, reward script, and owner key; + the new reward script in ProUpShareTx; and the new voting key in + ProUpSharedRegTx. +14. Proof-of-Service ban and revival of a shared masternode, including revival + with a null legacy `keyIdOwner`. ## Security Considerations -**Non-enforcing chains.** The template is anyone-can-spend at the script layer. +Non-enforcing chains. The template is anyone-can-spend at the script layer. On any chain that does not enforce this DIP — non-upgraded software, a chain split, a hypothetical future consensus regression — shared collateral outputs are freely spendable. The protection is the hard fork itself: theft can only @@ -692,20 +720,22 @@ are freely spendable. The protection is the hard fork itself: theft can only trade-off is inherent to consensus covenants on a chain without script-level covenant support and must be accepted with eyes open. -**Key separation.** The share owner key is control plane, never a fund +Key separation. The share owner key is control plane, never a fund destination. The central guarantee — owner-key compromise cannot steal principal — holds only because the refund destination is independent of the owner key: a thief who dissolves as the victim still pays the principal to a script they cannot spend. This is why refund and reward scripts must not pay to -share owner keys, and why wallets must not derive refund destinations from -owner keys even in forms consensus cannot detect (such as inside P2SH). +share owner keys. Consensus can enforce this separation for direct P2PKH +destinations, but cannot inspect the spending conditions behind P2SH. Wallets +must keep the owner key independent from the keys controlling either kind of +destination. -**Key compromise.** A stolen share owner key allows redirecting that share's +Key compromise. A stolen share owner key allows redirecting that share's future rewards and triggering a penalized unilateral dissolution — never principal theft. Because keys cannot be rotated, victim and thief hold identical capabilities; the honest holder always retains the exit right. -**Key loss.** Principal is never endangered: any other participant's unilateral +Key loss. Principal is never endangered: any other participant's unilateral dissolution refunds the key-less participant in full, and outside the early period this costs the rescuer only the transaction fee. A key-less participant cannot force exit, one lost key permanently disables the unanimous and @@ -713,44 +743,43 @@ whole-masternode update paths, and losing all owner keys freezes the collateral forever. Standby dissolutions (see above) reduce all of these to "keep your standby with your refund-key backup". -**Hostage and griefing dynamics.** A participant who refuses to sign -whole-masternode updates can degrade the node (for example, blocking operator -key replacement), and an honest leaver both pays the early penalty and pays a -pro-rata slice of it to the obstructive participant. This is bounded: the +Obstruction and forced exits. A participant who refuses to sign whole-masternode +updates can degrade the node (for example, by blocking operator-key +replacement), and an honest leaver both pays the early penalty and distributes +a pro-rata portion of it to the obstructive participant. This is bounded: the initially configured operator key keeps working indefinitely, exit after the early period is penalty-free, and the early penalty is a term every participant -consented to at formation. Wallets must present penalty terms prominently, and -must warn that a zero `earlyPenalty` permits cheap early griefing. +consented to at formation. Wallets must present penalty terms prominently and +warn that a zero `earlyPenalty` makes it inexpensive to force an early exit. -**Transaction-identifier malleability.** The covenant input's empty-`scriptSig` +Transaction-identifier malleability. The covenant input's empty-`scriptSig` rule and the low-S requirement on all payload signatures pin every free byte of a ProDisTx; inputs, sequences, outputs, and lock time are covered by the signed digest. Without these rules, third parties could mutate a pending dissolution's txid, breaking child-pays-for-parent fee bumps. -**Forbidden destinations.** The list in +Forbidden destinations. The list in [Forbidden Template Destinations](#forbidden-template-destinations) is consensus-critical: missing any entry creates either permanently frozen -collateral or an unconstructable coinbase (a consensus deadlock) via a crafted -script. +collateral or a coinbase that is impossible to construct (a consensus deadlock) +via a crafted script. ## Privacy Considerations Shared ownership is consensus-visible by construction: -* the share table — amounts, owner key IDs, refund scripts, reward scripts, and +* The share table — amounts, owner key IDs, refund scripts, reward scripts, and penalty terms — is public on-chain forever; -* every payout block creates up to 8 owner outputs that persistently cluster +* Every payout block creates up to 8 owner outputs that persistently cluster the co-owners' scripts with each other and with the masternode; -* the immutable refund script is an address pre-commitment made years before +* The immutable refund script is an address pre-commitment made years before the exit that eventually pays it. Wallets should use fresh keys and destinations for every formation, never reuse a refund destination across formations or with other on-chain activity, and may -rotate reward scripts freely. Participants who want unlinkability between their -share and their other funds must break the link before funding (for example, -by mixing the funding inputs); note that refunds return as large, -non-denominated outputs. +rotate reward scripts freely. Participants who do not want their share linked to +other funds must break the link before funding (for example, by mixing the +funding inputs); note that refunds return as large, non-denominated outputs. ## Copyright diff --git a/project-words.txt b/project-words.txt index b6312e7d..d1d0936d 100644 --- a/project-words.txt +++ b/project-words.txt @@ -113,8 +113,6 @@ topups tprv tpub unretrievable -unconstructable -unlinkability trustlessly UTXO varint From 165b6c3d1008174f51bc26073462eded82bb3977 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 17 Aug 2026 11:01:01 -0500 Subject: [PATCH 06/12] docs: cap unilateral dissolution fees and penalty overpayment 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 --- dip-pasta-shared-masternode-collateral.md | 52 ++++++++++++++++++----- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index a55bb44d..3c435bc6 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -300,10 +300,12 @@ non-increasing in `spendHeight`. #### Output rules -All output rules are minimum-based: paying more penalty than required is -always valid. Because `requiredPenalty` is non-increasing, a ProDisTx that is -valid at height `h` is valid at every height `h' >= h`: no dissolution is -silently invalidated by chain progress. A unilateral ProDisTx is invalid +All output rules are minimum-based, with two fixed ceilings: paying more +penalty than required is valid up to the configured `earlyPenalty`, and the +transaction fee is capped. Because `requiredPenalty` is non-increasing and +both ceilings are height-independent, a ProDisTx that is valid at height `h` +is valid at every height `h' >= h`: no dissolution is silently invalidated by +chain progress. A unilateral ProDisTx is invalid whenever it pays less than `requiredPenalty`. A zero-penalty standby becomes valid when the required penalty reaches zero, or immediately when `earlyPenalty` is zero. @@ -319,13 +321,22 @@ Let `a = actorIndex`, `W` = the sum of non-actor share amounts, and 3. For each non-actor output, with `bonus[i] = value[i] - shares[i].amount`: `bonus[i] >= floor(P * shares[i].amount / W)`. 4. The sum of all `bonus[i]` is at least `P`. +5. The transaction fee — the collateral value minus the sum of all outputs — + is at most `MAX_DIS_FEE` = 1000000 duffs (0.01 DASH). +6. In a unilateral dissolution, the sum of all `bonus[i]` is at most + `earlyPenalty`. The multiplication in rule 3 must use an intermediate at least 128 bits wide; a signed 64-bit intermediate can overflow for valid amounts. Everything not paid to these outputs is the transaction fee, which by value -conservation can only come from the actor's share; the actor output is -therefore implicitly capped at `shares[a].amount - P`. +conservation can only come from the actor's share. Rules 5 and 6 cap the two +paths by which value can leave the actor's share beyond the required penalty — +fees and voluntary penalty overpayment — so a unilateral dissolution pays the +actor output at least `shares[a].amount - earlyPenalty - MAX_DIS_FEE`. Rule 6 +does not constrain unanimous dissolutions: every share owner has signed the +exact outputs, so the participants may jointly distribute any bonuses that +respect the per-share minimums, subject only to the fee cap. The per-recipient minimum in rule 3 uses a plain floor with no remainder assignment: floors are element-wise monotone in `P`, whereas any remainder @@ -348,7 +359,8 @@ Dash relay supports neither BIP125 replacement nor package relay, so a ProDisTx must embed a sufficient fee at signing time; child-pays-for-parent on the actor output can only raise the mining priority of a ProDisTx that already meets the minimum relay fee. A flat fee of roughly 100000 duffs — about one millionth of -the minimum share — provides large fee-market headroom. Because Dash relay has +the minimum share, and a tenth of the consensus fee ceiling `MAX_DIS_FEE` — +provides large fee-market headroom. Because Dash relay has no replacement mechanism, the first ProDisTx to reach the mempool for a given collateral wins; a competing ProDisTx conflicts on the collateral input and is rejected until the first is mined or dropped. @@ -616,6 +628,22 @@ with a non-increasing required penalty make validity monotone: once valid, always valid. This property is also what makes standby dissolutions safe to sign years in advance. +### Why unilateral fees and penalty overpayment are capped + +The output minimums alone leave two unbounded paths by which value could leave +the actor's share: the transaction fee and voluntary penalty overpayment. +Without ceilings, one stolen share owner key could sign a unilateral +dissolution that pays the victim's entire share to miners as fee, or as +"bonus" concentrated on a colluding participant — principal theft through the +very transaction meant to make it impossible. The fee ceiling is a fixed +constant, and the bonus ceiling is the configured `earlyPenalty` rather than +the height-dependent `requiredPenalty`: both ceilings are stable while the +required minimum only loosens with height, preserving monotone validity, so an +early-period standby paying the full `earlyPenalty` remains valid forever. +With the ceilings, key compromise costs the actor at most +`earlyPenalty + MAX_DIS_FEE` — the forced exit the penalty terms already +price, plus a small constant. + ### Why the penalty is redistributed rather than burned or waived Redistribution compensates remaining participants for losing their node and @@ -679,10 +707,12 @@ Implementations should include tests for at least the following: 6. Valid: unilateral dissolution during the early period paying `earlyPenalty`; unilateral dissolution after the early period paying no penalty; a dissolution signed during the early period confirming after the boundary - (monotonicity); overpaying the penalty; unanimous dissolution paying no - penalty at any height. + (monotonicity); overpaying the penalty up to `earlyPenalty`; unanimous + dissolution paying no penalty at any height; unanimous dissolution paying + bonuses above `earlyPenalty`. 7. Invalid: unilateral dissolution paying no penalty inside the early period; any per-recipient floor violation; bonus sum below the required penalty; + a unilateral bonus sum above `earlyPenalty`; a fee above `MAX_DIS_FEE`; redirected or reordered refund outputs; extra outputs; extra inputs; non-empty `scriptSig` on the collateral input; wrong actor signature; signature count other than 1 or `sharesCount`; unanimous signatures out of @@ -732,7 +762,9 @@ destination. Key compromise. A stolen share owner key allows redirecting that share's future rewards and triggering a penalized unilateral dissolution — never -principal theft. Because keys cannot be rotated, victim and thief hold +uncapped principal theft. The dissolution fee and bonus ceilings bound the +loss at `earlyPenalty + MAX_DIS_FEE`; every other duff must reach the actor's +immutable refund script. Because keys cannot be rotated, victim and thief hold identical capabilities; the honest holder always retains the exit right. Key loss. Principal is never endangered: any other participant's unilateral From 4a5e8ba8a44c8f6904a42c6965c7f8256867fc4f Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 17 Aug 2026 11:01:25 -0500 Subject: [PATCH 07/12] docs: pin digest serialization and payload hash conventions 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 --- dip-pasta-shared-masternode-collateral.md | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index 3c435bc6..4d55f52e 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -217,13 +217,21 @@ replaced here: "DashSharedMNReg" || payload version || tx version || tx type || tx nLockTime || inputsHash || all input sequences || outputsHash || -type || mode || netInfo and Platform fields || +type || mode || netInfo || keyIdVoting || pubKeyOperator || operatorReward || shares || earlyPeriodBlocks || earlyPenalty ``` where `inputsHash` and `outputsHash` are as defined in DIP-0003, and `shares` -is the serialized share table. Consent therefore binds every participant to the +is the share table exactly as serialized in the payload, including the leading +`sharesCount` byte. Every field uses its payload or transaction serialization, +and the leading tag is serialized as a compactSize-length-prefixed string, not +as bare bytes. Shared registrations are type 0, so no Platform payload fields +exist to hash. `collateralOutpoint` is deliberately absent: only one output of +the transaction may carry the template (see +[Collateral Spend Enforcement](#collateral-spend-enforcement)), so +`outputsHash` already fixes the collateral slot. Consent binds every +participant to the exact funding inputs (prevouts and sequences), all outputs (including the collateral output and every change output), the full share table, the penalty terms, and the registrar configuration. Consent never relies on the sighash @@ -284,7 +292,8 @@ proTxHash || actorIndex || sigCount The digest commits to the transaction's actual input and outputs directly; the payload deliberately carries no `inputsHash`/`outputsHash` copies. It also commits to `sigCount`, which selects the mode, so the unilateral/unanimous -distinction cannot be malleated after signing. +distinction cannot be malleated after signing. The tag is serialized as a +compactSize-length-prefixed string, like `SharedRegConsentHash`'s. #### Required penalty @@ -399,7 +408,9 @@ Share `amount`, `refundScript`, `ownerKeyID`, the participant count, the penalty, and the early period are immutable for the life of the masternode. The new reward script is subject to the same script-type, payee-reuse, and template restrictions as at registration. A ProUpShareTx referencing a non-shared -masternode is invalid. +masternode is invalid. The payload hash is the double-SHA256 of the payload +serialized with the signature fields (`payloadSigSize` and `payloadSig`) +omitted entirely, following the DIP-0003 convention for update payloads. ### Updating the Shared Registrar (ProUpSharedRegTx) @@ -423,7 +434,11 @@ A ProUpSharedRegTx updates the whole-masternode registrar fields and requires one valid signature from every current share owner key, in share order. Operator-key change semantics (service reset) match ProUpRegTx in DIP-0003. A ProUpSharedRegTx referencing a non-shared masternode is invalid, and a plain -ProUpRegTx referencing a shared masternode is invalid. +ProUpRegTx referencing a shared masternode is invalid. The payload hash each +owner signs is the double-SHA256 of the payload serialized with `sigCount` and +`sigs` omitted entirely, following the same DIP-0003 convention. Unlike +ProDisTx, `sigCount` is not committed into the digest; it needs no commitment +because it must equal the immutable `sharesCount`. The operator reward is fixed at registration and is not updatable, matching the DIP-0003 ProUpRegTx model (where `operatorReward` is likewise immutable after From 6999fbad6b573ceb901234ca16a9a3d7f27b1345 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 17 Aug 2026 11:01:43 -0500 Subject: [PATCH 08/12] docs: make zeroed shared fields normative for non-shared payloads 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 --- dip-pasta-shared-masternode-collateral.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index 4d55f52e..da4e84ee 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -102,15 +102,15 @@ This DIP is therefore a strict superset of DIP-0026: ### Provider Transaction Version -This DIP extends the unreleased version 3 (extended addresses) ProRegTx payload, -which carries the DIP-0026 `payouts` field. A version 3 ProRegTx with a non-zero +This DIP extends the version 3 (extended addresses) ProRegTx payload, which +carries the DIP-0026 `payouts` field. A version 3 ProRegTx with a non-zero `sharesCount` is a shared registration. A version 3 ProRegTx with -`sharesCount = 0` behaves exactly as specified in DIP-0026. - -Because version 3 has not been released, the additional fields below change the -serialization of all version 3 ProRegTx payloads (a non-shared payload still -serializes `sharesCount = 0`, an empty share list, and zeroed penalty fields). -This DIP and DIP-0026 must therefore deploy in the same release. +`sharesCount = 0` behaves exactly as specified in DIP-0026; it must serialize +no shares, no `joinSigs`, `earlyPeriodBlocks = 0`, and `earlyPenalty = 0`, and +a non-zero value in any of these fields is invalid. The fields below are part +of the serialization of every version 3 ProRegTx payload, so this DIP and +DIP-0026 deploy together in the same release (see +[Deployment and Compatibility](#deployment-and-compatibility)). ### Shared Collateral Script @@ -706,7 +706,8 @@ Implementations should include tests for at least the following: 2. Invalid: shared Evo registration; external shared collateral; share sum not equal to the collateral; share below 100 DASH; `earlyPenalty >= min(share)`; early period above the cap; non-empty DIP-0026 `payouts` with shares; - non-zero `keyIdOwner`. + non-zero `keyIdOwner`; a non-shared version 3 payload with non-zero + `joinSigs`, `earlyPeriodBlocks`, or `earlyPenalty`. 3. Invalid: duplicate share owner key within the table or across masternodes (both directions with normal masternodes); duplicate refund scripts; refund or reward script paying P2PKH to any share owner key or the voting From 77624156cc9d6ee5648933fc6da5c5c57f1ae55a Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 17 Aug 2026 11:01:57 -0500 Subject: [PATCH 09/12] docs: extend payee-reuse rules to registrar voting-key updates 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 --- dip-pasta-shared-masternode-collateral.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index da4e84ee..d4e9ab28 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -440,6 +440,12 @@ owner signs is the double-SHA256 of the payload serialized with `sigCount` and ProDisTx, `sigCount` is not committed into the digest; it needs no commitment because it must equal the immutable `sharesCount`. +The new `keyIdVoting` is subject to the registration payee-reuse rule in +reverse: its P2PKH form must not equal any share's `refundScript` or effective +reward script (the reward script in use, falling back to the refund script +when empty). Without this check, a registrar update could silently re-create +the payee collision that registration rule 10 forbids. + The operator reward is fixed at registration and is not updatable, matching the DIP-0003 ProUpRegTx model (where `operatorReward` is likewise immutable after registration); it is therefore not carried in this payload. @@ -742,7 +748,9 @@ Implementations should include tests for at least the following: 10. ProUpShareTx updates exactly one reward script with the correct single signature; invalid for wrong signer, non-shared masternode, or any attempt to alter immutable fields. ProUpSharedRegTx requires all signatures in - share order; plain ProUpRegTx is invalid for a shared masternode. + share order; plain ProUpRegTx is invalid for a shared masternode; a + ProUpSharedRegTx whose new voting key is paid by any share's refund or + effective reward script is invalid. 11. Reward split across mined blocks: conservation, remainder to the last entry, zero-output omission, operator reward interaction, reward script fallback to refund script, and amounts large enough to overflow a 64-bit From 46c4142e90e4dfc4465df5428adcf475b1c7d11f Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 17 Aug 2026 11:02:10 -0500 Subject: [PATCH 10/12] docs: pin same-block validation context for share updates 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 --- dip-pasta-shared-masternode-collateral.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index d4e9ab28..71e4048f 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -527,6 +527,12 @@ implementations must enforce, at both mempool acceptance and block connection: block in either order: the update always applies before the removal takes effect. +Within a block, provider transactions are validated and applied sequentially +against the evolving list: a ProUpShareTx or ProUpSharedRegTx may follow its +masternode's registration in the same block. Because removal takes effect only +in the collateral-spend phase, share owner keys freed by a ProDisTx become +reusable by a new registration only from the following block. + Mempool implementations should additionally evict pending ProUpShareTx and ProUpSharedRegTx transactions for a masternode when its ProDisTx confirms. @@ -741,7 +747,9 @@ Implementations should include tests for at least the following: share order. 8. Invalid: registration and dissolution of the same masternode within one block (a dissolution is valid only once its registration is contained in a - prior block). + prior block); a registration reusing a share owner key freed by a ProDisTx + in the same block. Valid: a registration followed by a share update of the + same masternode within one block. 9. A pending dissolution remains valid across confirming ProUpShareTx and ProUpSharedRegTx transactions; a standby dissolution signed at registration broadcasts successfully after the early-period boundary. From 82a2c31da63c6f3a1d0ac55ef47c7a641ee57ece Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 17 Aug 2026 11:02:22 -0500 Subject: [PATCH 11/12] docs: add wallet guidance for dissolution lock times 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 --- dip-pasta-shared-masternode-collateral.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index 71e4048f..b5f26d80 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -295,6 +295,16 @@ commits to `sigCount`, which selects the mode, so the unilateral/unanimous distinction cannot be malleated after signing. The tag is serialized as a compactSize-length-prefixed string, like `SharedRegConsentHash`'s. +The digest commits `nLockTime` and the input sequence, but consensus does not +otherwise restrict them, and special transactions use transaction version 3, +so BIP68 relative lock-time semantics apply to the collateral input's +sequence. Participants who want a time-locked dissolution may use one; the +monotone validity described below then holds from the first height at which +the transaction is also final. Wallets must verify `nLockTime` and the input +sequence before signing — in particular when co-signing a unanimous +dissolution built by another participant, and when signing a standby +dissolution intended to be broadcastable immediately. + #### Required penalty ```text From 79ed4abe3ffed6f08346bb6c6ec997fe13f889cf Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 17 Aug 2026 11:02:34 -0500 Subject: [PATCH 12/12] docs: drop release-time framing from the version rationale 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 --- dip-pasta-shared-masternode-collateral.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dip-pasta-shared-masternode-collateral.md b/dip-pasta-shared-masternode-collateral.md index b5f26d80..1653d477 100644 --- a/dip-pasta-shared-masternode-collateral.md +++ b/dip-pasta-shared-masternode-collateral.md @@ -704,10 +704,10 @@ zero. ### Why this extends provider transaction version 3 -Version 3 (extended addresses, which carries DIP-0026's payouts) has not shipped, -so using the same version avoids an additional payload version solely for field -layering, at the cost of a wire-format change to an unreleased payload. The two -DIPs deploy together in v24. +Folding shared collateral into version 3 (extended addresses, which carries +DIP-0026's payouts) avoids an additional payload version solely for field +layering. The two DIPs deploy together in v24, so every version 3 payload +carries both field sets. ### Bounds