diff --git a/README.md b/README.md index 3e8d1cc4..69353c41 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ DIP stands for Dash Improvement Proposal. Similar to Bitcoin's [BIPs](https://github.com/bitcoin/bips/), a DIP is a design document providing information to the Dash community, or describing a new feature for Dash or its processes or environment. The DIP should provide a concise technical specification of the feature and a rationale for the feature. -Because Dash is forked from the Bitcoin codebase, many of the BIPs can be applied to Dash as well (a list of the BIPs updated to include Dash-specific details can be found [here](https://github.com/dashevo/bips)). The purpose of the DIPs is not to duplicate those which exist as BIPs, but to introduce protocol upgrades or feature specifications which are unique to Dash. +Because Dash is forked from the Bitcoin codebase, many of the BIPs can be applied to Dash as well (a list of the [BIPs updated to include Dash-specific details](https://github.com/dashevo/bips)). The purpose of the DIPs is not to duplicate those which exist as BIPs, but to introduce protocol upgrades or feature specifications which are unique to Dash. ## Contributions @@ -47,6 +47,7 @@ Number | Layer | Title | Owner | Type | Status [29](dip-0029.md) | Consensus | Randomness Beacon For LLMQ Selection | Virgile Bartolo | Standard | Proposed [30](dip-0030.md) | Consensus | Replay Attack Prevention and State Transition Nonces | Samuel Westrich | Standard | Proposed [31](dip-0031.md) | Consensus | Platform Proof of Service | Ivan Shumkov, Pasta | Standard | Proposed +[TBD](dip-decentralized-masternode-shares.md) | Consensus | Decentralized Masternode Shares | Pasta | Standard | Draft ## License diff --git a/dip-0002/special-transactions.md b/dip-0002/special-transactions.md index f1b8a743..79819970 100644 --- a/dip-0002/special-transactions.md +++ b/dip-0002/special-transactions.md @@ -4,8 +4,8 @@ The transaction type is described based on proposed DIPs. Here is a table of current proposed types and their associated DIP. Future DIPs may introduce more types. -*Note:* This table refers to the _payload_ version which relates only to the special transaction -payload and is distinct from the _transaction_ version. +*Note:* This table refers to the *payload* version which relates only to the special transaction +payload and is distinct from the *transaction* version. | Type | Transaction Type | DIP Number and Name | Payload Version | State | | ---- | ---------------- | ------------------- | --------------- | ----- | @@ -18,3 +18,6 @@ payload and is distinct from the _transaction_ version. | 7 | Masternode Hard Fork Signal | [DIP 023: Enhanced Hard Fork Mechanism](https://github.com/dashpay/dips/blob/master/dip-0023.md) | 1 | Active | | 8 | Asset Lock | [DIP 027: Dash Core Credit Pool](https://github.com/dashpay/dips/blob/master/dip-0027.md) | 1 | Active | | 9 | Asset Unlock | [DIP 027: Dash Core Credit Pool](https://github.com/dashpay/dips/blob/master/dip-0027.md) | 1 | Active | +| 10 | Provider Dissolution Transaction (ProDisTx) | [DIP TBD: Decentralized Masternode Shares](../dip-decentralized-masternode-shares.md) | 1 | Proposed | +| 11 | Provider Update Share Transaction (ProUpShareTx) | [DIP TBD: Decentralized Masternode Shares](../dip-decentralized-masternode-shares.md) | 1 | Proposed | +| 12 | Provider Update Shared Registrar Transaction (ProUpSharedRegTx) | [DIP TBD: Decentralized Masternode Shares](../dip-decentralized-masternode-shares.md) | 1 | Proposed | diff --git a/dip-decentralized-masternode-shares.md b/dip-decentralized-masternode-shares.md new file mode 100644 index 00000000..e9237a78 --- /dev/null +++ b/dip-decentralized-masternode-shares.md @@ -0,0 +1,1575 @@ +
+  DIP: TBD
+  Title: Decentralized Masternode Shares
+  Author(s): Pasta
+  Comments-Summary: No comments yet.
+  Status: Draft
+  Type: Standard
+  Created: 2026-06-20
+  Requires: DIP-0002, DIP-0003, DIP-0023, DIP-0026, DIP-0028
+  License: MIT License
+
+ +## Table of Contents + +1. [Abstract](#abstract) +2. [Motivation](#motivation) +3. [Prior Work](#prior-work) +4. [Relationship to DIP-0026](#relationship-to-dip-0026) +5. [Specification](#specification) + 1. [Terminology](#terminology) + 2. [Parameters](#parameters) + 3. [Shared-Collateral Mode Discriminator](#shared-collateral-mode-discriminator) + 4. [Shared Collateral Output](#shared-collateral-output) + 5. [Collateral Share](#collateral-share) + 6. [Shared Registration (ProRegTx v4, shared mode)](#shared-registration-proregtx-v4-shared-mode) + 7. [Registration Consent Digest](#registration-consent-digest) + 8. [Shared Update Transactions](#shared-update-transactions) + 9. [Authorization Tiers](#authorization-tiers) + 10. [Reward Distribution](#reward-distribution) + 11. [Dissolution (ProDisTx)](#dissolution-prodistx) + 12. [Collateral Spend Enforcement](#collateral-spend-enforcement) + 13. [Deterministic Masternode State](#deterministic-masternode-state) + 14. [Simplified Masternode List and Filters](#simplified-masternode-list-and-filters) + 15. [Governance](#governance) +6. [Deployment and Compatibility](#deployment-and-compatibility) +7. [Rationale](#rationale) +8. [Test Cases](#test-cases) +9. [Implementation Notes](#implementation-notes) +10. [Security Considerations](#security-considerations) +11. [Open Issues](#open-issues) +12. [Copyright](#copyright) + +## Abstract + +This DIP extends [DIP-0003: Deterministic Masternode Lists](dip-0003.md) by defining a +**shared-collateral mode** within the existing DIP-0026 v4 provider transaction payload, selected by +an explicit `isSharedCollateral` discriminator flag. In shared-collateral mode, between 2 and 8 +mutually distrusting participants jointly fund, operate, and exit a single Regular or Evolution +masternode under protocol-enforced consent. The shared masternode is funded by an internal +collateral output that is locked by consensus to a dissolution covenant. Owner rewards are split +natively in the coinbase by recorded share amounts. Each participant may unilaterally dissolve the +masternode at any time, subject to a participant-chosen penalty and the standard transaction fee, +and the full set of participants may dissolve unanimously with no penalty. Updates to fields that +affect all participants require consent from every participant; the operator role remains unchanged. + +Shared collateral is a strict superset of the [DIP-0026: Multi-Party Payouts](dip-0026.md) +reward-splitting mechanism. DIP-0026 v4 with `isSharedCollateral == false` leaves the registrar +owner in unilateral control of the payout list and is unchanged by this DIP. This DIP introduces the +shared-collateral discriminator into the same v4 payload version so that, when the discriminator is +true, share amounts, refund destinations, and the collateral itself are bound to per-participant +consent and cannot be redirected by any single owner, operator, miner, or compromised update path. +No new ProRegTx provider payload version is reserved. + +## Motivation + +Dash masternodes require 1000 DASH of collateral for a Regular masternode and 4000 DASH for an +Evolution masternode. Many users wish to combine smaller holdings to operate a single masternode and +receive a proportional share of rewards. Without protocol support, shared ownership requires an +off-chain custodian who controls the collateral, the registration keys, and the distribution of +rewards. This is a custodial relationship: the custodian can abscond with collateral, freeze a +participant out of rewards, refuse to exit, or be compelled to do any of the above. + +DIP-0026 makes the recurring reward split trustless, but a single registrar owner can still rewrite +the payout list, the collateral remains under one party's control, and there is no +consensus-enforced exit path. To make shared masternodes fully trustless, the protocol must also +enforce: + +* atomic, multi-party formation of the masternode and its collateral output; +* a collateral covenant that allows funds to leave only through a valid dissolution transaction; +* per-participant refund obligations on exit; +* per-share reward shares tied to recorded collateral contributions; +* an authorization model that distinguishes individual, shared-registrar, and operator-controlled + fields; and +* a unilateral exit path so that an unresponsive or hostile counterparty cannot freeze any + participant's principal. + +This DIP specifies those rules. The initial scope is deliberately narrow: internal collateral only, +immutable share amounts and participant set, immutable refund scripts, immutable participant owner +keys, per-share reward-script self-update, unanimous shared-registrar updates, operator-authorized +service and operator-payout updates, and unilateral or unanimous dissolution. Partial exit, +participant replacement, external collateral, owner-key rotation, refund-script mutation, relock +transactions, and on-chain fractional governance are out of scope and may be addressed by future +DIPs. + +## Prior Work + +* [DIP-0002: Special Transactions](dip-0002.md) +* [DIP-0003: Deterministic Masternode Lists](dip-0003.md) +* [DIP-0023: Enhanced Hard Fork Mechanism](dip-0023.md) +* [DIP-0026: Multi-Party Payouts](dip-0026.md) +* [DIP-0028: Evolution Masternodes](dip-0028.md) + +## Relationship to DIP-0026 + +DIP-0026 introduces provider transaction payload version `4` (`MultiPayout`). Under DIP-0026 v4 with +`isSharedCollateral == false`, the registrar owner remains the sole signer for ProRegTx and +ProUpRegTx, and may freely rewrite the payout list at any time. + +This DIP does not modify DIP-0026 v4 non-shared semantics. Shared masternodes reuse the same v4 +provider transaction payload version with `isSharedCollateral == true`, with the following +distinctions: + +| Property | v4, `isSharedCollateral == false` (DIP-0026) | v4, `isSharedCollateral == true` (this DIP) | +| --- | --- | --- | +| Ownership model | Single registrar owner | 2 to 8 participants | +| Payout list authority | Registrar owner via ProUpRegTx | Per-share self-update; share amounts immutable | +| Collateral lock | Single owner's UTXO | Shared collateral covenant | +| Exit path | Owner spends collateral | Valid `ProDisTx` only | +| Voting / operator updates | Owner | Unanimous participant consent | +| Refund on exit | None enforced | Per-participant refund script enforced | + +The two modes share `nVersion == 4` but are not interchangeable on the wire: the discriminator +selects mutually exclusive variant fields (see [Shared-Collateral Mode +Discriminator](#shared-collateral-mode-discriminator)), so a payload serialized with +`isSharedCollateral == true` MUST NOT be reinterpreted as a non-shared v4 payload, and a non-shared +v4 payload MUST NOT be reinterpreted as shared. A single-owner masternode using v3 or non-shared v4 +cannot be converted to shared in place: shared collateral is established only by a new v4 +registration with `isSharedCollateral == true`, and a shared masternode is wound down only by a +valid `ProDisTx`. There is no in-place conversion from a shared v4 masternode back to a non-shared +v3 or v4 masternode. + +## Specification + +### Terminology + +| Term | Definition | +| --- | --- | +| Participant | One of the 2 to 8 owners of a shared masternode, identified by a participant owner key. | +| Share | One participant's recorded collateral amount, refund script, reward script, and owner key. | +| Share table | The ordered list of `CollateralShare` entries in a shared registration. | +| Shared collateral | The internal collateral output of a shared masternode, locked to the shared-collateral script template. | +| Shared collateral script template | The fixed serialized output script used by every shared collateral output (see [Shared Collateral Output](#shared-collateral-output)). | +| Registration consent digest | The domain-separated digest each participant signs to authorize a shared registration. | +| Dissolution authorization digest | The domain-separated digest signed to authorize a `ProDisTx`. | +| Unilateral dissolution | A dissolution authorized by a single participant ("the actor") with a penalty redistributed to non-actors. | +| Unanimous dissolution | A dissolution authorized by every participant with no penalty. | +| Actor | The participant chosen by `actorIndex` who pays the transaction fee and, in unilateral mode, the penalty. | +| Early period | The first `earlyPeriodBlocks` eligible dissolution blocks after registration during which `earlyPenalty` applies to unilateral dissolution. Same-block dissolution is not eligible; if `earlyPeriodBlocks == 0`, no block is early. | + +### Parameters + +| Constant | Value | Description | +| --- | ---: | --- | +| `SHARED_MIN_PARTICIPANTS` | 2 | Minimum number of shares. | +| `SHARED_MAX_PARTICIPANTS` | 8 | Maximum number of shares. The limit is deliberately stricter than current legacy payout-list limits and is aligned with the pending DIP-0026 v4 design goal of bounding coinbase fan-out. | +| `SHARED_MIN_SHARE_DUFFS` | 1000000000 (10 DASH) | Minimum value of any `shares[i].amount`. | +| `SHARED_MAX_EARLY_PERIOD_BLOCKS` | 400305 (~2 years at 2.626 min/block) | Maximum value of `earlyPeriodBlocks`. | + +`SHARED_MAX_EARLY_PERIOD_BLOCKS` is given in blocks using Dash's measured long-run block interval +estimate of 2.626 minutes, yielding approximately `365 * 24 * 60 / 2.626 * 2 = 400305` blocks for a +two-year ceiling. Realized block times vary, so the wall-clock equivalent drifts with hashrate. +Implementations and tooling that prefer a wall-clock interface SHOULD convert the user-facing value +to blocks at the 2.626 min/block rate and reject any input that exceeds +`SHARED_MAX_EARLY_PERIOD_BLOCKS` once converted. + +`SHARED_MIN_SHARE_DUFFS` exists to keep recurring per-share coinbase outputs above policy dust +thresholds at present block rewards while permitting modest participation levels. Wallets SHOULD +warn participants that the block reward declines over time and very small shares may produce +dust-class outputs in the future. + +Shared collateral amounts MUST satisfy: + +```text +for every i: + MoneyRange(shares[i].amount) + SHARED_MIN_SHARE_DUFFS <= shares[i].amount <= GetMnType(nType).collat_amount +sum_overflow_safe(shares[i].amount) == GetMnType(nType).collat_amount +``` + +where `GetMnType(nType).collat_amount` is 1000 DASH for `Regular` or 4000 DASH for `Evo`, as defined +in DIP-0003 Appendix B and DIP-0028. The sum MUST be computed with exact overflow-safe arithmetic; +any overflow, non-`MoneyRange` share amount, share amount above the required collateral amount, or +non-exact sum is invalid. + +Penalty parameters MUST satisfy: + +```text +0 <= standardPenalty <= earlyPenalty +earlyPenalty < min(shares[i].amount) +standardPenalty < min(shares[i].amount) +``` + +The strict `<` ensures that even the smallest share has a positive remainder for the unilateral +actor after the penalty is paid, so a unilateral dissolution by any participant always returns a +non-zero amount to the actor before fees. + +### Shared-Collateral Mode Discriminator + +This DIP does NOT introduce a new ProRegTx provider payload version. Shared-collateral registration +reuses the existing DIP-0026 v4 ProRegTx (`nVersion == 4`) and selects shared-collateral semantics +by an explicit discriminator field, `isSharedCollateral`. This DIP introduces three new special +transaction types, each with its own independent payload version, to update and dissolve shared +masternodes. + +The discriminator is a single `uint8_t` field in the v4 ProRegTx payload, serialized immediately +after `nMode`. This wire position is a cross-DIP activation dependency: DIP-0026 v4 MUST reserve the +same discriminator byte for all v4 ProRegTx payloads before this DIP can activate (see [Open +Issues](#open-issues)). + +| Name | Type | Encoding | +| --- | --- | --- | +| `isSharedCollateral` | `uint8_t` | `0x00` for non-shared (DIP-0026 multi-payout) mode; `0x01` for shared-collateral mode. Any other value is invalid. | + +The discriminator selects mutually exclusive variant fields in the same v4 payload: + +* `isSharedCollateral == false`: after the shared discriminator byte reserved by DIP-0026 v4, the + remaining v4 payload follows the unchanged DIP-0026 multi-payout layout (single `keyIDOwner`, + `scriptPayout` / `payouts`, no share table, no shared collateral output, no penalty parameters, no + `earlyPeriodBlocks`). All DIP-0026 v4 semantics, validation, state, and authorization apply + verbatim and are unaffected by this DIP. +* `isSharedCollateral == true`: the v4 payload follows the shared-collateral layout defined in + [Shared Registration (ProRegTx v4, shared mode)](#shared-registration-proregtx-v4-shared-mode) (no + `keyIDOwner`, no `scriptPayout`, no `payouts`, a `CollateralShare[]` table, an internal collateral + output that pays `SHARED_COLLATERAL_SCRIPT`, and per-masternode penalty parameters). The + shared-collateral variant is selected solely by the discriminator; consensus MUST NOT infer the + variant from any other field length, script shape, or output count. + +The discriminator is the mode tag carried in the deterministic masternode state: a masternode +created by a v4 ProRegTx with `isSharedCollateral == true` carries `state.isSharedCollateral == +true` for the rest of its lifetime, and all shared-collateral state fields and authorization rules +in this DIP gate on that flag. A masternode created by a v4 ProRegTx with `isSharedCollateral == +false` carries `state.isSharedCollateral == false` and is governed by DIP-0026. + +The discriminator does NOT apply as the payload version of the new special transaction types +introduced below; those payloads carry their own, independent `nVersion` field that starts at `1` +(see each payload definition). The independent payload versions of `ProDisTx`, `ProUpShareTx`, and +`ProUpSharedRegTx` evolve separately from the v4 ProRegTx payload version. + +| Name | Value | Description | +| --- | ---: | --- | +| `TRANSACTION_PROVIDER_DISSOLVE` | 10 | `ProDisTx` payload (this DIP). | +| `TRANSACTION_PROVIDER_UPDATE_SHARE` | 11 | `ProUpShareTx` payload (this DIP). | +| `TRANSACTION_PROVIDER_UPDATE_SHARED_REGISTRAR` | 12 | `ProUpSharedRegTx` payload (this DIP). | + +Values `10`, `11`, and `12` are assigned by this DIP and are also registered in [DIP-0002's special +transaction type table](dip-0002/special-transactions.md). If another accepted DIP consumes any of +these values before this DIP is merged, this DIP MUST be updated to use the next free values before +merge; it MUST NOT ship with conflicting type assignments. + +ProRegTx (type `1`) is reused at payload version 4 with `isSharedCollateral == true` for shared +registration. The base `ProUpRegTx` (type `3`) is invalid against a shared-collateral masternode +(`state.isSharedCollateral == true`) and is unaffected for non-shared masternodes. The base +`ProUpServTx` (type `2`) continues to operate under DIP-0003 / DIP-0028 rules for non-shared +masternodes AND remains valid against a shared-collateral masternode for the operator-authorized +fields enumerated in [Authorization Tiers](#authorization-tiers); `ProUpServTx` MUST NOT attempt to +modify any owner-controlled or shared-collateral field. + +### Shared Collateral Output + +A shared registration creates exactly one internal collateral output, here called the **shared +collateral output**. The output value MUST equal `GetMnType(nType).collat_amount`. + +The shared collateral output uses a single, fixed serialized script, `SHARED_COLLATERAL_SCRIPT`, +with the following normative properties: + +1. It is a fixed byte sequence specified by this DIP; every shared collateral output on every + network uses the identical script bytes. Script-template equality is the registration-time + selector used to identify which output of a v4 shared-collateral ProRegTx is the shared + collateral output; it is NOT, on its own, the consensus identifier of a protected collateral. +2. It contains no participant-specific data. In particular, it does not embed any participant owner + key, refund script, reward script, share amount, or `proTxHash`. The per-masternode binding + between an outpoint and a `proTxHash` is provided by the deterministic masternode state, not by + the script itself. +3. It is recognizable by full nodes using a single template comparison, and it is recognizable as + non-standard by older nodes that do not understand shared collateral. +4. After activation, consensus and mempool policy protect the following shared collateral outpoint + sets: + * (a) **Registered shared collateral outpoints** — outpoints recorded + as the collateral of a registered shared-collateral masternode + (`state.isSharedCollateral == true`) in the deterministic + masternode list at the parent state of the block (or mempool tip) + being validated, including PoSe-banned entries that remain registered. + A spend of an outpoint in this class is rejected unless the spending + transaction is a valid `ProDisTx` for the corresponding masternode. + * (b) **Same-block pending shared collateral outputs** — shared + collateral outputs created by a valid v4 ProRegTx with + `isSharedCollateral == true` earlier in the block currently being + validated. A spend of an output in this class later in the same + block is rejected unconditionally: ordinary spends are rejected + because the outpoint represents a freshly created masternode + commitment, and `ProDisTx` is rejected because the registration is + not yet part of the parent deterministic state a dissolution would + consult. Dissolution of such a masternode is permitted only in a + strictly later block, after the registration has been recorded in + the deterministic masternode list. + * (c) **Mempool pending shared collateral outputs** — shared + collateral outputs created by an unconfirmed valid v4 ProRegTx with + `isSharedCollateral == true` in the mempool. A mempool spend of an + output in this class is rejected unconditionally until the + registration confirms. + Rejection is enforced at mempool acceptance, at block connection, and during deterministic + masternode list processing (see [Collateral Spend Enforcement](#collateral-spend-enforcement)). +5. Ordinary UTXOs that happen to pay `SHARED_COLLATERAL_SCRIPT` but are NOT in any protected class + above are NOT bound by the dissolution covenant. They behave as ordinary outputs at + script-evaluation time, subject to whatever spending conditions the recommended template imposes + (for example, the `OP_TRUE` redeem-script template makes them anyone-can-spend). Implementations + MUST NOT retroactively lock arbitrary pre-existing outputs or unrelated outputs created outside + the shared-collateral registration flow. +6. Before activation, upgraded relays and miners MUST treat any output with this script as + non-standard and SHOULD NOT mine it, so ordinary relay and mining policy discourages creating + such outputs before activation. After activation, the only way for a transaction to register an + outpoint into the protected set is a valid v4 ProRegTx with `isSharedCollateral == true`; relay + and mining policy MUST continue to treat any other output bearing `SHARED_COLLATERAL_SCRIPT` as + non-standard to discourage accidental creation of stranded anyone-can-spend outputs. + +The shared collateral script is the single-byte script `OP_TRUE`: + +```text +SHARED_COLLATERAL_SCRIPT = 0x51 +``` + +This bare anyone-can-spend script is deliberately non-standard except when it appears as the +collateral output of a valid v4 ProRegTx with `isSharedCollateral == true` after activation. +Script-level satisfaction is trivial, but consensus rules above script evaluation reject every +protected spend except a valid `ProDisTx`. Embedding per-masternode commitments (such as `proTxHash` +or participant keys) into the output script was considered and rejected because it would either +conflict with future relock-style updates of participant keys or require a separate commitment +scheme that would still depend on deterministic masternode state to verify. + +### Collateral Share + +```text +CollateralShare { + amount : CAmount (8 bytes, little-endian, in duffs) + refundScript : CScript (compact-size length-prefixed) + rewardScript : CScript (compact-size length-prefixed) + ownerKey : CKeyID (20 bytes) + joinSig : vector (compact-size length-prefixed, 65 bytes when present; CompactSize(0) in join-signature preimage) +} +``` + +| Field | Mutability | Authorization to change | +| --- | --- | --- | +| `amount` | Immutable | Set at registration; cannot be changed. | +| `refundScript` | Immutable | Set at registration; cannot be changed. | +| `rewardScript` | Mutable per-share | Participant owner key of that share, via `ProUpShareTx`. | +| `ownerKey` | Immutable | Set at registration; cannot be rotated in this DIP. | +| `joinSig` | Set only in `ProRegTx` payload | Validated at registration; not stored in deterministic state. In the registration join-signature digest, this field is serialized byte-exactly as an empty vector (`CompactSize(0)`), not omitted. | + +Field rules at registration: + +1. `amount` MUST satisfy `MoneyRange(amount)` and MUST be between `SHARED_MIN_SHARE_DUFFS` and + `GetMnType(nType).collat_amount`, inclusive. +2. `refundScript` MUST be a standard `P2PKH` or `P2SH` script. +3. `rewardScript` MUST be empty or a standard `P2PKH` or `P2SH` script. An empty `rewardScript` is + interpreted at the consensus layer as `refundScript`. +4. `ownerKey` MUST be distinct from every other `ownerKey` in the share table and from + `keyIDVoting`. +5. `ownerKey` MUST be distinct from every active owner key recorded for any other registered + masternode at the registration height. The unique-property index defined in DIP-0003 is extended + to track every `shares[i].ownerKey` for shared-collateral masternodes (see [Deterministic + Masternode State](#deterministic-masternode-state)). +6. `refundScript` MUST NOT be duplicated within the share table. +7. Effective reward scripts MUST NOT be duplicated within the share table. For this rule, an empty + `rewardScript` is interpreted as `refundScript` before duplicate detection. +8. No `refundScript` and no `rewardScript` may be a P2PKH script paying to `keyIDVoting` or to a key + ID that equals any `shares[i].ownerKey`. There is no `keyIDOwner` field in shared-collateral mode + — see [Shared Registration (ProRegTx v4, shared + mode)](#shared-registration-proregtx-v4-shared-mode). The intent matches the DIP-0026 key-reuse + rule: these scripts are spent in lower-trust wallet contexts and MUST NOT reuse keys that control + masternode state. +9. `joinSig` is a 65-byte compact ECDSA signature by `ownerKey` over the registration consent digest + defined in [Registration Consent Digest](#registration-consent-digest). Any other length or any + signature that does not verify under `ownerKey` is invalid. + +### Shared Registration (ProRegTx v4, shared mode) + +A v4 ProRegTx with `isSharedCollateral == true` uses the field layout below. Field positions for the +shared-collateral variant are specified normatively here; this list is exhaustive and defines the v4 +ProRegTx layout for `isSharedCollateral == true`. The v4 layout for `isSharedCollateral == false` is +unchanged from DIP-0026 and is NOT defined by this DIP. + +| Field | Type | Notes | +| --- | --- | --- | +| `nVersion` | `uint16_t` | MUST be `4`. | +| `nType` | `MnType` (`uint16_t`) | `Regular` or `Evo`. | +| `nMode` | `uint16_t` | MUST be `0`. | +| `isSharedCollateral` | `uint8_t` | MUST be `0x01` to select the shared-collateral variant defined in this section. `0x00` selects the unchanged DIP-0026 multi-payout variant and is not governed by this DIP. Any other value is invalid. | +| `collateralOutpoint.hash` | `uint256` | MUST be the null hash. | +| `collateralOutpoint.n` | `uint32_t` | Index of the shared collateral output within this transaction. | +| `netInfo` | DIP-0003/0028 net info | As for non-shared v4. | +| `pubKeyOperator` | BLS public key (basic scheme) | As for non-shared v4. | +| `keyIDVoting` | `CKeyID` | As for non-shared v4. | +| `nOperatorReward` | `uint16_t` | Basis points; MUST be from 0 to 10000. | +| `shares` | `CollateralShare[]` | CompactSize-prefixed vector of 2 to 8 entries; per [Collateral Share](#collateral-share). | +| `earlyPeriodBlocks` | `uint32_t` | `0` to `SHARED_MAX_EARLY_PERIOD_BLOCKS`. | +| `earlyPenalty` | `CAmount` (8 bytes) | Duffs. | +| `standardPenalty` | `CAmount` (8 bytes) | Duffs. | +| `inputsHash` | `uint256` | `CalcTxInputsHash(tx)`. | +| `platformNodeID`, `platformNetInfo` (Evo only) | as DIP-0028 | Serialized after `inputsHash` and before `vchSig`, preserving the DIP-0028 insertion point. | +| `vchSig` | `vector` | MUST be empty in shared-collateral mode (no external collateral signature). | + +There is no `keyIDOwner` field in a shared-collateral v4 ProRegTx. The role of the single owner key +in DIP-0003 is performed in shared-collateral mode by the collection of `shares[*].ownerKey`. The +non-shared v4 ProRegTx retains its `keyIDOwner` field unchanged. + +There is no `scriptPayout` or `payouts` field in a shared-collateral v4 ProRegTx. Owner rewards are +derived directly from the share table as specified in [Reward Distribution](#reward-distribution). +The non-shared v4 ProRegTx retains its DIP-0026 payout list unchanged. + +Unless explicitly replaced by this section, the base ProRegTx validation rules from DIP-0003, +DIP-0026, and DIP-0028 continue to apply to shared-collateral v4 registrations. This includes +network-info validation, Evo Platform-field validation, operator-key validity, and operator-key +uniqueness. + +A v4 ProRegTx with `isSharedCollateral == true` is invalid if any of the following conditions hold: + +1. `nVersion != 4`. +2. `isSharedCollateral` is neither `0x00` nor `0x01`. (When `isSharedCollateral == 0x00`, the + payload is governed by DIP-0026 and the remaining rules in this list do not apply.) +3. `nType` is not `Regular` or `Evo`. +4. `nMode != 0`. +5. `netInfo` fails the applicable DIP-0003 network-info validation rules. +6. For Evo masternodes, any Platform field fails the applicable DIP-0028 validation rules. +7. `pubKeyOperator` is not a valid basic-scheme BLS public key or collides with the operator key of + any other registered masternode. +8. `collateralOutpoint.hash` is not the null hash. +9. `collateralOutpoint.n` does not point to an output of this transaction. +10. The output at `collateralOutpoint.n` does not pay + `GetMnType(nType).collat_amount` to `SHARED_COLLATERAL_SCRIPT`. +11. The transaction creates more than one output paying + `SHARED_COLLATERAL_SCRIPT`. +12. `shares.size()` is less than `SHARED_MIN_PARTICIPANTS` or greater than + `SHARED_MAX_PARTICIPANTS`. +13. Any share fails its per-field validation rules in [Collateral + Share](#collateral-share). +14. Any `shares[i].amount` fails `MoneyRange`, is less than + `SHARED_MIN_SHARE_DUFFS`, or is greater than + `GetMnType(nType).collat_amount`. +15. The exact overflow-safe sum of all `shares[i].amount` values does not + equal `GetMnType(nType).collat_amount`, or the summation overflows. +16. `nOperatorReward > 10000`. +17. Penalty parameter constraints in [Parameters](#parameters) are not + satisfied. +18. `earlyPeriodBlocks > SHARED_MAX_EARLY_PERIOD_BLOCKS`. +19. `vchSig` is non-empty. +20. `inputsHash != CalcTxInputsHash(tx)`. +21. For any share `i`, `shares[i].joinSig` does not verify against + `shares[i].ownerKey` over the registration consent digest defined + below. + +Rules 19 and 20 are evaluated before any ECDSA signature verification. Consensus computes +`outputsHash` from the transaction outputs when building the registration consent digest; there is +no payload `outputsHash` field in a shared-collateral v4 ProRegTx. + +### Registration Consent Digest + +Each participant signs an explicit consent digest that commits to the full effect of the +registration transaction, independent of any sighash-mode behavior of the funding-input scripts. + +```text +SharedRegConsentHash = SHA256d( + "DashSharedMNReg" || + chainGenesisHash || + LE16(tx.nVersion) || LE16(tx.nType) || LE32(tx.nLockTime) || + inputsHash || sequencesHash || outputsHash || + LE16(payload.nVersion) || + LE16(payload.nType) || LE16(payload.nMode) || + LE8(payload.isSharedCollateral) || + LE32(payload.collateralOutpoint.n) || + netInfoSerialized || + platformFieldsSerialized || // present iff nType == Evo + pubKeyOperatorSerialized || + keyIDVoting || + LE16(nOperatorReward) || + LE8(shares.size()) || + sharesWithoutJoinSigs || + LE32(earlyPeriodBlocks) || + LE64(earlyPenalty) || + LE64(standardPenalty) +) +``` + +The digest commits explicitly to `payload.isSharedCollateral` so that a signature produced under one +variant cannot be reinterpreted under the other. + +Where: + +* `LE8`, `LE16`, `LE32`, `LE64` denote little-endian encodings of the indicated width. +* `SHA256d(x)` is the Dash double-SHA-256 used for transaction hashes. +* `chainGenesisHash` is the 32-byte block hash of the genesis block of the network on which the + registration is being authorized (mainnet, testnet, devnet, regtest, or any future network with a + distinct genesis block). Consensus uses the genesis hash of the chain that is validating the + transaction. Each participant signer MUST independently verify the network / chain context of the + registration before producing `joinSig`; a signature produced against one `chainGenesisHash` MUST + NOT verify on a network with a different genesis hash. +* `inputsHash` is `CalcTxInputsHash(tx)`. Consensus MUST recompute this from the transaction and + compare it to the `payload.inputsHash` field before signature verification; mismatch is invalid. +* `sequencesHash` is the SHA-256d of the concatenation of every input's `nSequence` in + transaction-input order, each encoded as `LE32(nSequence)`. Consensus MUST recompute this from the + transaction before signature verification. This prevents a coordinator from changing finality, + RBF, or `nLockTime` behavior after participants produce `joinSig`. +* `outputsHash` is the SHA-256d of the concatenation of every output of `tx` in order, each + serialized as `(value, scriptPubKey)` using the standard Dash transaction-output serialization. + Consensus MUST recompute this from the transaction; there is no `outputsHash` field on a ProRegTx, + but the recomputed value is used inside this digest. +* `netInfoSerialized` and `platformFieldsSerialized` use the same encoding as in the provider + transaction payload itself. +* `pubKeyOperatorSerialized` is the basic-scheme BLS public key serialization (48 bytes). +* `sharesWithoutJoinSigs` is the concatenation of every `CollateralShare` in share order, with each + `joinSig` field serialized byte-exactly as an empty vector (`CompactSize(0)`) so that signatures + are not self-referential. The field MUST NOT be omitted from the preimage. + +The domain separator `"DashSharedMNReg"` is the byte sequence of those ASCII characters with no +trailing NUL. + +Funding-input signatures are still required to authorize spending of each participant's funding +UTXOs, but consensus MUST NOT rely on those signatures to demonstrate consent to shared-masternode +parameters: Dash inherits Bitcoin sighash modes including `SIGHASH_NONE`, `SIGHASH_SINGLE`, and +`SIGHASH_ANYONECANPAY`, any of which permits later modification of fields a participant would +otherwise believe they had committed to. + +### Shared Update Transactions + +Two new special transaction types specialize updates for shared masternodes. The base `ProUpRegTx` +(type `3`) is invalid for shared-collateral masternodes (`state.isSharedCollateral == true`), +because its single-owner authorization model is incompatible with the shared-registrar tier defined +here. The base `ProUpServTx` (type `2`) remains valid for shared-collateral masternodes with +operator authorization (see [Authorization Tiers](#authorization-tiers)). + +#### ProUpShareTx (type 11) + +A `ProUpShareTx` updates exactly one share's mutable fields. In this DIP the only mutable per-share +field is `rewardScript`. + +```text +CProUpShareTx { + nVersion : uint16_t // MUST be 1 (payload version of this new special tx type) + proTxHash : uint256 + shareIndex : uint16_t // index into shares[] + newRewardScript : CScript // compact-size length-prefixed + inputsHash : uint256 + sig : vector // 65 bytes +} +``` + +`nVersion` here is the independent payload version of the new `TRANSACTION_PROVIDER_UPDATE_SHARE` +special transaction; it is NOT the v4 ProRegTx provider payload version. The two version namespaces +are separate, and the `ProUpShareTx` payload version evolves independently of the v4 ProRegTx +payload version. + +Validation: + +1. `nVersion` MUST be `1`. +2. The masternode identified by `proTxHash` MUST exist and MUST satisfy `state.isSharedCollateral == + true`. +3. `shareIndex` MUST be less than `shares.size()` in the current state. +4. `newRewardScript` MUST be empty or a standard `P2PKH` or `P2SH` script. An empty + `newRewardScript` is interpreted as `shares[shareIndex].refundScript`. +5. `newRewardScript` MUST satisfy the same key-reuse restrictions as registration: it MUST NOT be a + P2PKH paying to any `shares[i].ownerKey` and MUST NOT pay to `keyIDVoting`. +6. After interpreting an empty `newRewardScript` as `shares[shareIndex].refundScript`, the resulting + effective reward script MUST NOT equal any other share's current effective reward script. +7. `inputsHash` MUST equal `CalcTxInputsHash(tx)`. +8. `sig` MUST be a valid ECDSA compact signature by `shares[shareIndex].ownerKey` over: + + ```text + SHA256d("DashSharedMNUpShare" || + chainGenesisHash || + LE16(tx.nVersion) || LE16(tx.nType) || LE32(tx.nLockTime) || + inputsHash || sequencesHash || outputsHash || + LE16(payload.nVersion) || + proTxHash || LE16(shareIndex) || newRewardScriptSerialized) + ``` + + `chainGenesisHash` is the 32-byte genesis block hash of the network validating the transaction. + `payload.nVersion` is the ProUpShareTx payload version (currently `1`). `sequencesHash` and + `outputsHash` use the same definitions as in [Registration Consent + Digest](#registration-consent-digest) and are recomputed from the actual transaction before + signature verification. `newRewardScriptSerialized` is the standard compact-size length-prefixed + `CScript` serialization used in the payload. + +A valid `ProUpShareTx` replaces `shares[shareIndex].rewardScript` in the deterministic masternode +state. It does not revive a PoSe-banned masternode and does not affect any other field. Duplicate +effective reward scripts are therefore invalid both at registration and after every per-share +update. + +#### ProUpSharedRegTx (type 12) + +A `ProUpSharedRegTx` updates fields that affect all participants. In this DIP the mutable +shared-registrar fields are `pubKeyOperator`, `keyIDVoting`, and `nOperatorReward`. + +```text +CProUpSharedRegTx { + nVersion : uint16_t // MUST be 1 (payload version of this new special tx type) + proTxHash : uint256 + pubKeyOperator : CBLSPublicKey // basic scheme + keyIDVoting : CKeyID + nOperatorReward : uint16_t + inputsHash : uint256 + vchSigs : vector> // exactly shares.size() entries, in share order +} +``` + +As with `ProUpShareTx`, this `nVersion` is the independent payload version of the new +`TRANSACTION_PROVIDER_UPDATE_SHARED_REGISTRAR` special transaction and is unrelated to the v4 +ProRegTx provider payload version. + +Validation: + +1. `nVersion` MUST be `1`. +2. The masternode identified by `proTxHash` MUST exist and MUST satisfy `state.isSharedCollateral == + true`. +3. `pubKeyOperator` MUST be a valid basic-scheme BLS public key and MUST NOT collide with the + operator key of any other registered masternode. +4. `keyIDVoting` MUST be non-null and MUST NOT equal any `shares[i].ownerKey`. +5. No current `refundScript` and no current effective `rewardScript` may be a P2PKH script paying to + `keyIDVoting`. For this check, an empty `rewardScript` is interpreted as the corresponding + `refundScript`. +6. `nOperatorReward <= 10000`. +7. `inputsHash` MUST equal `CalcTxInputsHash(tx)`. +8. `vchSigs.size()` MUST equal `shares.size()`. +9. For each `i` in `[0, shares.size())`, `vchSigs[i]` MUST be a valid ECDSA compact signature by + `shares[i].ownerKey` over: + + ```text + SHA256d("DashSharedMNUpSharedReg" || + chainGenesisHash || + LE16(tx.nVersion) || LE16(tx.nType) || LE32(tx.nLockTime) || + inputsHash || sequencesHash || outputsHash || + LE16(payload.nVersion) || + proTxHash || pubKeyOperatorSerialized || + keyIDVoting || LE16(nOperatorReward)) + ``` + + `chainGenesisHash` is the 32-byte genesis block hash of the network validating the transaction. + `payload.nVersion` is the ProUpSharedRegTx payload version (currently `1`). `sequencesHash` and + `outputsHash` use the same definitions as in [Registration Consent + Digest](#registration-consent-digest) and are recomputed from the actual transaction before + signature verification. + +10. Signature verification proceeds in share order. Any missing, extra, or + out-of-order signature is invalid. + +A valid `ProUpSharedRegTx` replaces `pubKeyOperator`, `keyIDVoting`, and `nOperatorReward` in the +deterministic masternode state. It does not revive a PoSe-banned masternode and does not affect +share contents or collateral. + +If `pubKeyOperator` is unchanged, existing operator-controlled service fields +(`scriptOperatorPayout`, `netInfo`, and Platform identifiers) are unchanged by this transaction and +continue to be updated by the operator using `ProUpServTx`. + +If `pubKeyOperator` changes, all operator-controlled service fields MUST be reset: +`scriptOperatorPayout` becomes empty, `netInfo` becomes the empty network-info value for the +masternode's state version, Platform identifiers become null/empty, and the masternode is marked +PoSe-banned until the new operator submits a valid `ProUpServTx`. This mirrors the existing +operator-key-reset behavior for non-shared masternodes and prevents stale operator payout or +endpoint state from carrying across an operator change. + +### Authorization Tiers + +| Field or action | Authorization | Transaction | +| --- | --- | --- | +| Unilateral dissolution | One signature: `shares[actorIndex].ownerKey` | `ProDisTx` (mode `0`) | +| Unanimous dissolution | One signature per participant in share order | `ProDisTx` (mode `1`) | +| `pubKeyOperator` | One signature per participant in share order | `ProUpSharedRegTx` | +| `keyIDVoting` | One signature per participant in share order | `ProUpSharedRegTx` | +| `nOperatorReward` | One signature per participant in share order | `ProUpSharedRegTx` | +| `rewardScript` for share `i` | `shares[i].ownerKey` | `ProUpShareTx` | +| `refundScript` for share `i` | Immutable | — | +| `ownerKey` for share `i` | Immutable | — | +| `shares[i].amount`, share count, penalties, `earlyPeriodBlocks` | Immutable | — | +| `netInfo` (addresses) | Operator BLS key | `ProUpServTx` | +| `scriptOperatorPayout` | Operator BLS key | `ProUpServTx` | +| Platform service endpoints (Evo) | Operator BLS key | `ProUpServTx` | +| PoSe revocation | Operator BLS key | `ProUpRevTx` | + +`ProUpRegTx` (type `3`) is invalid against a shared-collateral masternode (`state.isSharedCollateral +== true`). `ProUpServTx` and `ProUpRevTx` continue to use operator authorization as in DIP-0003 and +DIP-0028. The operator can stop service or revoke the masternode but cannot redirect owner rewards +or spend the shared collateral. + +### Reward Distribution + +Owner-reward distribution for a shared-collateral masternode (`state.isSharedCollateral == true`) +reuses the DIP-0026 pipeline but weights by share amount rather than by basis points. + +1. Compute the masternode reward and apply the Platform credit-pool reallocation as in current + rules. +2. If `nOperatorReward != 0` and the operator has set `scriptOperatorPayout`, compute + `operatorAmount = floor(masternodeReward * nOperatorReward / 10000)` and subtract from + `masternodeReward` to obtain `ownerReward`. Otherwise `operatorAmount = 0` and `ownerReward = + masternodeReward`. +3. Distribute `ownerReward` across `shares` using the helper `DistributeByWeight(ownerReward, + shares[i].amount)` defined below. +4. For each share `i` whose computed reward is non-zero, create one coinbase output paying that + amount to: + * `shares[i].rewardScript`, if `shares[i].rewardScript` is non-empty; + * `shares[i].refundScript`, otherwise. +5. If `operatorAmount != 0`, create the operator payout output to `scriptOperatorPayout` using + current rules. + +```text +DistributeByWeight(total, weights): + sum_w = sum(weights) + require sum_w > 0 // reject all-zero weights + remainder_index = greatest i where weights[i] > 0 + out[i] = floor(total * weights[i] / sum_w) for each i != remainder_index + out[remainder_index] = total - sum(out[i] for i != remainder_index) + return out +``` + +The final positive-weight entry receives the rounding remainder, matching the shape of DIP-0026's +final-entry remainder rule while allowing weights to be collateral amounts rather than basis points. +`DistributeByWeight` is invalid for an all-zero weight vector and callers MUST NOT invoke it in that +case. All intermediate arithmetic in `DistributeByWeight`, including `total * weights[i]`, `sum_w`, +and `sum(out[i])`, MUST be evaluated exactly without overflow. Implementations MUST use at least +128-bit integer intermediates or a mathematically equivalent overflow-safe quotient/remainder +algorithm that produces the same floor and remainder results for every consensus-valid input. For +unanimous dissolution, where the penalty itself is zero, the bonus is taken to be all-zero directly +without calling `DistributeByWeight` (see [Dissolution (ProDisTx)](#dissolution-prodistx)). + +Coinbase validation requires every expected per-share reward output by exact amount and script for +every shared-collateral masternode scheduled in the block. As in DIP-0026, the relative order of +outputs within the coinbase is not consensus-significant. + +The `DistributeByWeight` helper is also used in [Dissolution (ProDisTx)](#dissolution-prodistx) for +penalty redistribution. + +### Dissolution (ProDisTx) + +A `ProDisTx` is the only transaction that can spend a shared collateral output after activation. It +is special transaction type `TRANSACTION_PROVIDER_DISSOLVE` (`10`). + +```text +CProDisTx { + nVersion : uint16_t // MUST be 1 (payload version of this new special tx type) + proTxHash : uint256 + mode : uint8_t // 0 = unilateral, 1 = unanimous + actorIndex : uint16_t + inputsHash : uint256 + outputsHash : uint256 + vchSigs : vector> +} +``` + +`nVersion` here is the independent payload version of the new `TRANSACTION_PROVIDER_DISSOLVE` +special transaction and is unrelated to the v4 ProRegTx provider payload version. + +#### Transaction shape + +A valid dissolution transaction satisfies all of: + +1. `nVersion` MUST be `1`. +2. It has exactly one input, and that input spends the shared collateral outpoint of the masternode + identified by `proTxHash`. No additional funding inputs are permitted in this initial scope. +3. Its outputs are the refund outputs defined below, in share order, with no additional outputs of + any kind. Change outputs are not permitted. +4. Its `nLockTime` MAY be non-zero. The dissolution authorization digest commits to `nLockTime`, so + consensus enforces whatever value was signed. +5. `mode` MUST be either `0` (unilateral) or `1` (unanimous). Any other value is invalid. + +#### Authorization digest + +```text +SharedDisHash = SHA256d( + "DashSharedMNDissolve" || + chainGenesisHash || + LE16(tx.nVersion) || LE16(tx.nType) || LE32(tx.nLockTime) || + LE32(tx.vin[0].nSequence) || + inputsHash || outputsHash || + LE16(payload.nVersion) || + proTxHash || + LE8(mode) || LE16(actorIndex) +) +``` + +Where `inputsHash` is `CalcTxInputsHash(tx)` and `outputsHash` is the SHA-256d of the concatenated +serialized outputs of `tx`, both recomputed from the actual transaction. The digest also commits to +the sequence of the single dissolution input (`tx.vin[0].nSequence`) so that changing the sequence +cannot disable or alter the signed `nLockTime` semantics. Consensus MUST recompute both hashes and +reject mismatches with the payload's `inputsHash` and `outputsHash` fields before any signature is +verified. `chainGenesisHash` is the 32-byte genesis block hash of the network validating the +transaction, identical to its use in [Registration Consent Digest](#registration-consent-digest); a +dissolution signed against one `chainGenesisHash` MUST NOT verify on a network with a different +genesis hash. Each signer MUST independently verify the network / chain context before producing its +`vchSigs` entry. `payload.nVersion` here is the dissolution payload version (currently `1`), not the +v4 ProRegTx provider payload version. + +#### Period and penalty + +Let `H` be the height at which the dissolution is connected, and let `state` be the deterministic +masternode state for `proTxHash` at the parent block of `H`. Define: + +```text +early = (state.earlyPeriodBlocks > 0) && + ((H - state.nRegisteredHeight) <= state.earlyPeriodBlocks) +penalty = (mode == 1) ? 0 : (early ? state.earlyPenalty : state.standardPenalty) +``` + +Because same-block dissolution is invalid, the first eligible dissolution height is +`state.nRegisteredHeight + 1`. Therefore `earlyPeriodBlocks == 1` means only that first eligible +block uses `earlyPenalty`, and `earlyPeriodBlocks == 0` disables the early penalty window. + +#### Signature cardinality + +For `mode == 0` (unilateral): + +1. `actorIndex < state.shares.size()`. +2. `vchSigs.size() == 1`. +3. `vchSigs[0]` MUST verify under `state.shares[actorIndex].ownerKey` over `SharedDisHash`. + +For `mode == 1` (unanimous): + +1. `actorIndex < state.shares.size()`. +2. `vchSigs.size() == state.shares.size()`. +3. For each `i`, `vchSigs[i]` MUST verify under `state.shares[i].ownerKey` over `SharedDisHash`. + Signatures are processed in share order; any missing, extra, or out-of-order signature is + invalid. + +#### Output covenant + +Let `a = actorIndex` and `N = shares.size()`. Define the non-actor bonus distribution: + +```text +if mode == 1: + // unanimous: penalty is zero, bonus is trivially zero everywhere. + // DistributeByWeight is NOT invoked. + bonus[i] = 0 for all i in [0, N) +else if mode == 0: + // unilateral: redistribute the penalty pro rata among non-actors. + // At least one non-actor weight is positive because N >= 2 and every + // share amount is positive, so sum_w > 0 and DistributeByWeight is + // well-defined. + weights[i] = (i == a) ? 0 : state.shares[i].amount + bonus = DistributeByWeight(penalty, weights) +``` + +Define the per-share dissolution value: + +```text +value[i] = (i == a) ? actorValue + : state.shares[i].amount + bonus[i] +``` + +where `actorValue` is the value chosen by the constructor of the transaction, subject to: + +```text +0 <= actorValue <= state.shares[a].amount - penalty +``` + +The outputs of the dissolution transaction MUST be exactly, in order matching the share table with +the actor slot optionally omitted: + +* Walk the share table in ascending `i` from `0` to `N - 1`. For each `i`: + * If `i != a`, the transaction MUST contain the next output at this + position, paying exactly `state.shares[i].amount + bonus[i]` to + `state.shares[i].refundScript`. + * If `i == a` and `actorValue > 0`, the transaction MUST contain the + next output at this position, paying exactly `actorValue` to + `state.shares[a].refundScript`. + * If `i == a` and `actorValue == 0`, the actor's output MUST be + omitted entirely. No dust, OP_RETURN, or placeholder output stands + in for the actor. + +Non-actor outputs always appear in share order and are exact; the actor's output appears in its +share-order slot when present and is absent otherwise. No outputs other than those defined above are +permitted. In particular, OP_RETURN outputs, change outputs, and operator outputs are not permitted. + +Consensus matches outputs by walking the share table in share order and either consuming the next +output (for non-actor slots and for the actor slot when present) or skipping the slot entirely (only +for the actor when `actorValue` would be zero). Any deviation — extra output, missing non-actor +output, wrong refund script, wrong amount, or a stray actor output when the value would be zero — is +invalid. + +For `mode == 1` (unanimous), `penalty` is zero, every non-actor output pays exactly +`state.shares[i].amount` to `state.shares[i].refundScript`, and the actor output pays `actorValue` +with `0 <= actorValue <= state.shares[a].amount` (or is omitted when `actorValue == 0`). + +#### Fee accounting + +The difference between the spent shared collateral value and the sum of all outputs is the +transaction fee: + +```text +fee = GetMnType(state.nType).collat_amount - sum(outputs.value) +``` + +Because every non-actor output is exact and the actor output is bounded above by +`state.shares[a].amount - penalty`, this fee can be taken only from the actor's share. The fee MUST +be non-negative; consensus enforces this by the actor-output upper bound and the no-extra-output +rule. + +A zero-fee dissolution is consensus-valid. Relay and mining policy MAY require a non-zero fee at the +standard minimum relay fee rate; wallets SHOULD construct dissolutions paying the current standard +fee so that miners include them. + +#### Same-block ordering + +A `ProDisTx` MUST NOT appear in the same block as the v4 ProRegTx with `isSharedCollateral == true` +that creates the shared collateral output it would spend. Dissolution operates against the +deterministic masternode state at the parent block, so the registering masternode is not yet part of +that state until the registering block has been connected. A `ProDisTx` whose input refers to a +shared collateral output created earlier in the same block is therefore invalid even if every other +covenant rule would otherwise hold; dissolution is permitted only in a strictly later block. Block +validation MUST track shared collateral outputs created earlier in the same block solely so that any +spend of those outputs later in the same block — ordinary or `ProDisTx` — can be rejected (see +[Collateral Spend Enforcement](#collateral-spend-enforcement)). + +#### State effect + +Connecting a valid `ProDisTx` removes the masternode identified by `proTxHash` from the +deterministic masternode list as a normal collateral-spend removal would. The masternode entry MUST +NOT be removed until the corresponding `ProDisTx` has been validated. + +### Collateral Spend Enforcement + +Provider transaction `CheckSpecialTx` validation alone is insufficient to enforce the shared +collateral covenant: an ordinary transaction that spends the shared collateral outpoint of a +registered shared-collateral masternode (`state.isSharedCollateral == true`) but bears no +special-transaction payload would, under DIP-0003 rules, simply remove the masternode by collateral +spend. Implementations MUST also enforce the rules below outside `CheckSpecialTx`. + +The protected set has three parts with different rules: + +* **Registered set.** Every collateral outpoint recorded against a registered shared-collateral + masternode (`state.isSharedCollateral == true`) in the deterministic masternode list at the parent + of the block (or mempool tip) being validated, including PoSe-banned entries that remain + registered until removed by a valid `ProDisTx`. A spend of an outpoint in the registered set is + rejected unless it is a valid `ProDisTx` for the matching masternode. +* **Same-block pending set.** Every shared collateral outpoint created by a valid v4 ProRegTx with + `isSharedCollateral == true` earlier in the block currently being validated. A spend of an + outpoint in the same-block pending set is rejected unconditionally: ordinary spends are rejected + because the outpoint represents an active masternode commitment, and `ProDisTx` spends are + rejected because the registration is not yet part of the parent deterministic state. Dissolution + of such a masternode is permitted only in a strictly later block. +* **Mempool pending set.** Every shared collateral outpoint created by an unconfirmed v4 ProRegTx + with `isSharedCollateral == true` accepted into the mempool. A mempool transaction that spends an + outpoint in this set is rejected unconditionally until the registration confirms and the outpoint + moves into the registered set. + +A UTXO whose `scriptPubKey` equals `SHARED_COLLATERAL_SCRIPT` but whose outpoint is in neither set +is NOT protected by the covenant and is not the subject of the rules below. The rules deliberately +key off the recorded outpoint identity, not raw script equality, to avoid retroactively locking +pre-existing or unrelated outputs that happen to match the template. + +1. **Mempool acceptance.** Before accepting any transaction into the mempool, scan its inputs. For + each input that spends an outpoint in the registered set, reject the transaction unless it is a + valid `ProDisTx` for the masternode whose collateral outpoint matches the spent outpoint in the + current deterministic masternode list. For each input that spends an outpoint in the mempool + pending set, reject the transaction unconditionally, including if the transaction is a + `ProDisTx`. The same-block pending set is computed per block by block-connection logic; the + mempool pending set prevents miners from assembling an invalid parent-plus-child package around + an unconfirmed shared-collateral registration. +2. **Block connection, prior blocks.** Before applying the deterministic masternode list update for + a connected block, scan every non-coinbase transaction in that block for inputs that spend + outpoints in the registered set (collateral outpoints of registered shared-collateral + masternodes, including PoSe-banned entries, in the parent-state deterministic masternode list). + Reject the block unless every such input is the input of a valid `ProDisTx` for the matching + masternode. +3. **Block connection, same-block.** Maintain a per-block index of shared collateral outputs created + by valid v4 ProRegTx with `isSharedCollateral == true` earlier in the same block, keyed by + `(txid, vout, proTxHash)`. For every later non-coinbase transaction in the same block, reject any + input that spends such an output. Both ordinary spends and `ProDisTx` spends are rejected: a + `ProDisTx` whose input refers to a shared collateral output created in the same block is invalid + even if every other covenant rule would otherwise hold. +4. **Deterministic masternode list removal.** Replace the "remove on collateral spend" rule from + DIP-0003 with the following for shared-collateral masternodes: a shared-collateral masternode + (`state.isSharedCollateral == true`) MUST NOT be removed until a corresponding valid `ProDisTx` + for that `proTxHash` has been processed in a strictly later block than the block that contained + the registering v4 ProRegTx. Any spend of the masternode's shared collateral outpoint by a + transaction that is not such a `ProDisTx` is invalid. +5. **Block disconnection and reorg.** Disconnecting a block that contained a `ProDisTx` MUST restore + the shared-collateral masternode entry to the exact pre-dissolution deterministic masternode + state. State diffs MUST capture the full share vector and shared-registration parameters as a + single replacement (see [Deterministic Masternode State](#deterministic-masternode-state)) so + that reorg replay is deterministic. + +These rules are evaluated before script-level evaluation for inputs that spend outpoints in the +registered set or same-block pending set: even if the underlying redeem script is trivially +satisfiable, consensus rejects the spend of a registered-set outpoint unless the spend is a valid +`ProDisTx` for the corresponding masternode, and rejects every spend of a same-block pending +outpoint unconditionally. Mempool policy likewise rejects every spend of a mempool-pending outpoint +before script-level standardness or validity can make the child acceptable. Spends of unprotected +UTXOs that happen to bear the same script are not subjected to the covenant and are script-evaluated +normally. + +### Deterministic Masternode State + +The deterministic masternode state defined in DIP-0003 is extended for shared-collateral +masternodes. The pre-shared and non-shared serialization is unchanged. + +The deterministic masternode state for every masternode includes the `isSharedCollateral` flag +carried from its registering v4 ProRegTx (or implicitly `false` for masternodes registered under +earlier provider payload versions). When `state.isSharedCollateral == true`, the masternode state +includes the following additional fields: + +| Field | Type | Notes | +| --- | --- | --- | +| `shares` | `CollateralShare[]` (without `joinSig`) | 2 to 8 entries; order preserved from registration. | +| `earlyPeriodBlocks` | `uint32_t` | Frozen at registration. | +| `earlyPenalty` | `CAmount` | Frozen at registration. | +| `standardPenalty` | `CAmount` | Frozen at registration. | + +The fields `scriptPayout` and `payouts` are unused when `state.isSharedCollateral == true` and MUST +be serialized as empty. `keyIDOwner` is absent from the shared-collateral v4 ProRegTx and MUST NOT +participate in shared-collateral validation or uniqueness indexes. If an implementation's +deterministic state object contains a legacy `keyIDOwner` field, that field MUST be serialized as +null/zero whenever `state.isSharedCollateral == true` and MUST be ignored whenever +`state.isSharedCollateral` is true. + +State diffs MUST be gated on `state.isSharedCollateral`: + +1. The state-diff bitfield gains a new field bit for the share vector (exact bit value deferred; see + [Open Issues](#open-issues)). Any change to any field of any `shares[i]` (including a per-share + `rewardScript` change from a `ProUpShareTx`) MUST produce a state diff in which the entire share + vector is fully replaced. This full-replacement encoding is the single canonical representation + of a shared-collateral share-state diff: compact diffs, per-share field diffs, and any other + alternative encoding of share-vector changes are NOT permitted. Implementations MUST emit and + accept share-vector changes only as a full replacement of the share vector. +2. State diffs for shared-collateral fields are present only when `state.isSharedCollateral == + true`; for non-shared masternodes (both pre-v4 and v4 with `isSharedCollateral == false`) the + corresponding bits MUST NOT appear. +3. Snapshot serialization MUST round-trip across restart, reorg replay, and historic block + validation without loss. + +Unique-property indexes MUST be extended: + +* Every `shares[i].ownerKey` participates in the owner-key uniqueness index used to reject reuse + across masternodes at registration time. Non-shared masternodes contribute their single + `keyIDOwner`; shared-collateral masternodes contribute every participant owner key. +* No network-wide uniqueness index is added for `keyIDVoting`. As in DIP-0003, the voting key may be + delegated and reused; shared-collateral validation only forbids `keyIDVoting` from equaling any + participant owner key for the same masternode. +* The operator-key uniqueness index applies to `pubKeyOperator` as in DIP-0003. + +A shared-collateral masternode is created only by a valid v4 ProRegTx with `isSharedCollateral == +true` and removed only by a valid `ProDisTx` for the same `proTxHash`. Non-shared masternodes +continue to be created and removed as in DIP-0003 and DIP-0026. + +### Simplified Masternode List and Filters + +`CSimplifiedMNListEntry::CalcHash`, as used by DIP-0004 simplified masternode list verification, +MUST NOT include shares, refund scripts, reward scripts, penalty parameters, or any other +shared-collateral-only field. Light clients receive no SML commitment to shared-collateral metadata. +Diagnostic RPCs and extended JSON output MAY expose shared-collateral fields. + +The trust boundary this creates is intentional and mirrors the direction DIP-0026 took for +multi-payout metadata: shared-collateral fields are not committed to by SML hashes, so light clients +cannot independently verify share amounts, refund scripts, reward scripts, penalty parameters, or +any other shared-collateral-only field from an SML proof. A future extension to DIP-0004 would be +required before SPV clients could verify shared-collateral terms without trusting a serving full +node. + +Full nodes are unaffected by this boundary: a full node validates every shared-collateral field from +the deterministic masternode state that it reconstructs by replaying blocks, exactly as it does for +non-shared masternode state. The SML/filter limitation applies only to clients that depend on SML +hashes or filter matches as their source of truth. + +Special-transaction and bloom filtering, as defined in DIP-0003 for `ProRegTx`/`ProUpRegTx` payout +scripts, is extended for shared-collateral transactions: + +| Transaction | Filter elements | +| --- | --- | +| `ProRegTx` v4 with `isSharedCollateral == true` | Every `shares[i].refundScript`; every non-empty `shares[i].rewardScript`; every `shares[i].ownerKey`; `keyIDVoting`; `pubKeyOperator`; the shared collateral outpoint. | +| `ProUpShareTx` | `proTxHash`; the current `shares[shareIndex].ownerKey`; `newRewardScript` when non-empty. | +| `ProUpSharedRegTx` | `proTxHash`; every current `shares[i].ownerKey`; `keyIDVoting`; `pubKeyOperator`. | +| `ProDisTx` | `proTxHash`; every signing participant owner key required by `mode`; every refund output `scriptPubKey`, matching existing transaction-output filter semantics. | + +These filter extensions exist solely for client-side discovery and relay; a filter match is NOT a +consensus commitment to the matched data, and any light client that uses a filter match as +authoritative evidence of shared-collateral state still depends on the honesty of the serving full +node. + +These filter extensions apply only to shared-collateral special transactions after activation. +Non-shared special transactions (including DIP-0026 v4 with `isSharedCollateral == false`) retain +their existing DIP-0003 and DIP-0026 filtering. + +### Governance + +A shared-collateral masternode (`state.isSharedCollateral == true`) retains one `keyIDVoting`, +signed by the voting key recorded in deterministic masternode state. This DIP does not introduce +fractional participant voting or per-share governance keys. + +Vote weight remains defined by existing masternode-type rules. A shared `Regular` masternode has the +regular masternode vote weight, while a shared `Evo` masternode retains the DIP-0028 evonode vote +weight of four relative to a regular masternode. The single shared-collateral `keyIDVoting` +authorizes that whole masternode-type vote weight. + +`keyIDVoting` MAY be updated only by `ProUpSharedRegTx`, which requires unanimous participant +signatures. There is no protocol-level mechanism for fractional voting among participants; any +coordination of participant preferences for governance votes is off-chain and outside the scope of +this DIP. + +## Deployment and Compatibility + +**Draft status.** This DIP is a Draft. The DIP-0023 deployment name and signaling window are NOT +finalized in this revision. The consensus model and covenant rules below are written to be +reviewable as a draft and MUST NOT be treated as activation-ready until deployment parameters are +assigned and this DIP is updated accordingly (see [Open Issues](#open-issues)). + +Activation of this DIP is gated by an intended future deployment defined under +[DIP-0023](dip-0023.md). The exact deployment name and signaling window are assigned during release +engineering. This DIP does not claim any specific Dash Core release for activation; release +engineering MUST verify the live deployment state of any candidate fork bit before assigning +activation, and the activation target is subject to DIP editor assignment. + +Before activation: + +* Any v4 ProRegTx payload with `isSharedCollateral == true` is invalid. DIP-0026 v4 payloads with + `isSharedCollateral == false` are unaffected by this DIP and remain governed by DIP-0026. +* Special transaction types `10`, `11`, and `12` are invalid. +* Upgraded relay and mining policy MUST treat any output with `SHARED_COLLATERAL_SCRIPT` as + non-standard and SHOULD NOT mine it. Pre-activation consensus validity for such outputs is + otherwise unchanged unless a separate deployment says otherwise. + +After activation: + +* v4 ProRegTx with `isSharedCollateral == true`, `ProUpShareTx`, `ProUpSharedRegTx`, and `ProDisTx` + are valid as specified above. +* DIP-0003 single-owner masternodes (v1, v2, v3) and DIP-0026 v4 masternodes with + `isSharedCollateral == false` continue to operate unchanged. +* No conversion path is defined from a non-shared masternode (v1/v2/v3 or v4 with + `isSharedCollateral == false`) to a shared-collateral masternode. A shared-collateral masternode + is established only by a new v4 ProRegTx with `isSharedCollateral == true`; it is wound down only + by a `ProDisTx`. +* No conversion path is defined from a shared-collateral masternode back to a non-shared masternode. + +A shared-collateral masternode and a non-shared masternode never appear as the same `proTxHash`; +deterministic masternode state is gated on `state.isSharedCollateral` as specified in [Deterministic +Masternode State](#deterministic-masternode-state). + +## Rationale + +### Strict superset of DIP-0026 + +DIP-0026 fixes the operational pain of off-chain reward distribution but does not constrain the +registrar owner, the collateral UTXO, or the exit path. The most common use cases that motivate +DIP-0026 (services that hold the collateral on behalf of multiple beneficiaries) remain custodial +under v4 with `isSharedCollateral == false`. This DIP addresses that gap by binding share amounts, +refund destinations, and the collateral itself to per-participant consent. + +### Explicit discriminator instead of a distinct provider payload version + +A provider transaction payload version is a wire-format identifier, not a semantic mode flag. +Allocating a new ProRegTx provider payload version solely for shared collateral would conflate two +orthogonal axes: the on-the-wire layout of the v4 payload, and whether the masternode that v4 +payload describes is shared. The version number would then drift apart from any future layout +changes that are unrelated to shared collateral, and every downstream consumer would have to learn +to treat one specific version value as a stand-in for "shared". + +Selecting shared-collateral semantics by an explicit discriminator (`isSharedCollateral`) within the +existing v4 payload keeps the wire version describing only the wire format and keeps the semantic +mode selection a first-class field that callers, validators, indexers, and state diffs all read +directly. The discriminator is committed to by the registration consent digest and carried into +deterministic masternode state, so a participant signature, a state entry, and an SML query all +agree on the mode without inferring it from the version number, output shape, or script length. +Future layout-level changes to the v4 payload can advance the wire version without altering what +"shared" means; new shared-mode variants can advance the discriminator without consuming provider +payload version numbers. + +The discriminator also keeps the non-shared v4 variant unchanged after the shared discriminator byte +that DIP-0026 v4 must reserve for all v4 ProRegTx payloads: a v4 ProRegTx with `isSharedCollateral +== false` deserializes and validates exactly as DIP-0026 specifies after that byte, and the variant +fields specific to shared mode appear only when the discriminator selects them. + +### Internal collateral only + +External shared collateral would require either a multi-signature collateral UTXO controlled +off-chain (defeating the goal of trustless exit) or a covenant attached to a previously created +output (out of scope for the existing UTXO format). Constraining shared-collateral mode to internal +collateral makes the covenant model tractable and ensures that every shared collateral output is +created by a v4 ProRegTx with `isSharedCollateral == true` whose consent digest committed every +participant. + +### Immutable share parameters + +Mutable share amounts would let an attacker who compromised one owner key dilute another +participant's stake without unanimous consent. Mutable refund scripts would create the same risk for +the eventual exit destination. Both are therefore immutable in this DIP. `rewardScript` is mutable +per share because reward destinations are paid in lower-trust wallet contexts and benefit from key +rotation, and because a stale reward script affects only the participant who owns it. + +### Immutable participant owner keys + +If the shared collateral output embedded participant owner keys, owner-key rotation would invalidate +the output script or require a relock transaction that simultaneously spent the old collateral and +created a new one while preserving the masternode entry. Both options were considered out of scope +for the initial DIP. The simpler choice is specified here: participant owner keys are immutable, and +the collateral output script contains no participant-specific data. + +### Operator authority unchanged + +Service availability and operator payout are owned by the operator under DIP-0003. Moving those +fields to unanimous participant consent would prevent legitimate operator-driven operational changes +(such as IP or port reassignment) without coordinating every participant for routine maintenance, +while not actually improving the security model (the operator can already withhold service +unilaterally). Operator authority is therefore unchanged. + +### Explicit registration consent signatures + +Funding-input signatures cannot be relied on to authenticate consent to shared-masternode +parameters: Bitcoin/Dash sighash modes `SIGHASH_NONE`, `SIGHASH_SINGLE`, and `SIGHASH_ANYONECANPAY` +each permit later modification of fields a participant would otherwise believe they had signed. Each +participant therefore signs an explicit domain-separated consent digest that covers the inputs, +outputs, payload fields, share table, penalty parameters, and version fields. + +### Dissolution covenant commits to outputs + +A signature over only the payload would leave the actor remainder, fee, and even the refund +destinations open to substitution at relay or mining time without invalidating the payload +signature. The dissolution authorization digest therefore commits to the full transaction effect: +`nVersion`, `nType`, `nLockTime`, recomputed `inputsHash` and `outputsHash`, and the payload's +`proTxHash`, `mode`, and `actorIndex`. + +### Penalty bounds + +The strict `<` bounds on `earlyPenalty` and `standardPenalty` ensure that even the smallest +participant retains a positive remainder *before fees* after a unilateral exit, so that no consent +flow can configure a penalty that exceeds the actor's principal. This bounds the worst-case griefing +cost. The output covenant separately permits the actor's output to be omitted entirely when the +transaction fee consumes the full pre-fee remainder, so the strict `<` rule on penalties is not +required to keep the post-fee actor remainder positive; that flexibility exists so that the actor +can cover an arbitrary fee at relay time without violating the covenant. + +### Mempool and block enforcement + +Spend rejection cannot live only in `CheckSpecialTx`: ordinary transactions that spend the shared +collateral output would not invoke `CheckSpecialTx` at all, and the DIP-0003 "remove on collateral +spend" rule would otherwise quietly drop the masternode. The required mempool and block-validation +hooks are made explicit. + +### Same-block dissolution forbidden + +A `ProDisTx` is invalid in the same block as the v4 ProRegTx with `isSharedCollateral == true` that +creates the shared collateral output it would spend. Dissolution operates against the deterministic +masternode state at the parent block, so allowing same-block dissolution would require either a +mid-block speculative state machine or a duplicated validation path that reads the in-progress block +under construction. Forbidding the same-block case keeps state transitions atomic at block +boundaries, matches how DIP-0003 ProUp*Tx are evaluated against the parent state, and aligns the +protected-set rule with a single canonical share-vector diff (see [Deterministic Masternode +State](#deterministic-masternode-state)). A participant who wishes to exit immediately after +registration can do so in the next block at no additional cost beyond standard fees. + +### One voting key per masternode + +Fractional governance would require either a new vote-aggregation scheme or a per-share signing tier +on every governance message. Both are out of scope. Preserving one voting key per masternode while +retaining existing masternode-type vote weights keeps shared masternodes compatible with the current +governance infrastructure and defers fractional voting to a future DIP. + +## Test Cases + +Implementations SHOULD include at minimum the following tests. + +### Helper + +1. `DistributeByWeight(10000, [2500, 2500, 2500, 2500])` returns `[2500, 2500, 2500, 2500]`. +2. `DistributeByWeight(10001, [2500, 2500, 2500, 2500])` returns `[2500, 2500, 2500, 2501]`. +3. `DistributeByWeight(10, [0, 1, 1])` returns `[0, 5, 5]`. +4. `DistributeByWeight(7, [1, 1, 1, 1, 1, 1, 1, 1])` returns seven duffs to the final index and zero + to the other indices. +5. `DistributeByWeight(3, [0, 1, 0, 1, 0])` returns `[0, 1, 0, 2, 0]`: the final positive-weight + index receives the remainder. +6. `DistributeByWeight(5, [0, 0, 0])` is invalid (all-zero weights) and MUST be rejected by the + helper. + +### Registration + +1. A v4 ProRegTx with `isSharedCollateral == true` and two shares whose amounts sum to 1000 DASH, + both shares signed correctly, is valid. +2. A v4 ProRegTx with `isSharedCollateral == true` and eight shares summing to 1000 DASH, all + signed, is valid. +3. A v4 ProRegTx with `isSharedCollateral == true` and `nOperatorReward > 10000` is invalid. +4. A v4 ProRegTx with `isSharedCollateral == true` and one share is invalid. +5. A v4 ProRegTx with `isSharedCollateral == true` and nine shares is invalid. +6. A v4 ProRegTx with `isSharedCollateral == true` whose share amounts sum to 999.99 DASH is + invalid. +7. A v4 ProRegTx with `isSharedCollateral == true` whose share summation overflows is invalid. +8. A v4 ProRegTx with `isSharedCollateral == true` and any individual share amount outside + `MoneyRange` or greater than `GetMnType(nType).collat_amount` is invalid. +9. A v4 ProRegTx with `isSharedCollateral == true` and a duplicate participant owner key is invalid. +10. A v4 ProRegTx with `isSharedCollateral == true` and a duplicate refund + script is invalid. +11. A v4 ProRegTx with `isSharedCollateral == true` and duplicate effective + reward scripts is invalid. +12. A v4 ProRegTx with `isSharedCollateral == true` and a refund script + paying to a participant owner key is invalid. +13. A v4 ProRegTx with `isSharedCollateral == true` whose collateral output + uses a P2PKH script (not `SHARED_COLLATERAL_SCRIPT`) is invalid. +14. A v4 ProRegTx with `isSharedCollateral == true` whose `vchSig` is + non-empty is invalid. +15. A v4 ProRegTx with `isSharedCollateral == true` whose `joinSig` for + share `i` was produced under a different `outputsHash` is invalid. +16. A v4 ProRegTx with `isSharedCollateral == true` whose `joinSig` for + share `i` was produced under different input sequences is invalid. +17. A v4 ProRegTx with `isSharedCollateral == true` whose `joinSig` for + share `i` was produced under a different penalty value is invalid. +18. A v4 ProRegTx with `isSharedCollateral == true` whose `joinSig` for + share `i` was produced under `isSharedCollateral == false` is invalid; + the consent digest commits to the discriminator. +19. A v4 ProRegTx with `isSharedCollateral` set to a value other than `0x00` + or `0x01` is invalid. +20. A v4 ProRegTx with `isSharedCollateral == false` continues to be + validated by DIP-0026 unchanged and is unaffected by the rules in this + section. + +### Reward Splitting + +1. A shared-collateral masternode with shares `[500, 500]` DASH receives two coinbase outputs of + equal value to each `rewardScript`. +2. A shared-collateral masternode with shares `[300, 300, 400]` DASH receives three coinbase outputs + proportional to the shares, with rounding rules per `DistributeByWeight`. +3. A shared-collateral masternode with `nOperatorReward = 1000` subtracts the operator amount first, + then splits the remainder by share. +4. A coinbase missing any expected per-share output is invalid. +5. A coinbase with an extra unexpected output for a shared-collateral masternode is invalid. + +### Updates + +1. A `ProUpShareTx` signed by `shares[0].ownerKey` updating `shares[0].rewardScript` is valid. +2. A `ProUpShareTx` for share `0` signed by `shares[1].ownerKey` is invalid. +3. A `ProUpShareTx` setting `newRewardScript` to a P2PKH paying `shares[0].ownerKey` is invalid. +4. A `ProUpShareTx` setting `newRewardScript` to another share's current effective reward script is + invalid. +5. A `ProUpShareTx` whose signature was produced under different input sequences or transaction + outputs is invalid. +6. A `ProUpShareTx` with `nVersion != 1` is invalid. +7. A `ProUpSharedRegTx` with `vchSigs.size() == shares.size()`, signed by every share in order, + updating `nOperatorReward` to a value no greater than 10000, is valid. +8. A `ProUpSharedRegTx` with `nVersion != 1` is invalid. +9. A `ProUpSharedRegTx` setting `keyIDVoting` to null is invalid. +10. A `ProUpSharedRegTx` setting `nOperatorReward > 10000` is invalid. +11. A `ProUpSharedRegTx` setting `keyIDVoting` to a key hash already used by + any current refund script or effective reward script is invalid. +12. A `ProUpSharedRegTx` whose signature was produced under different input + sequences or transaction outputs is invalid. +13. A `ProUpSharedRegTx` missing one signature is invalid. +14. A `ProUpSharedRegTx` with an extra signature is invalid. +15. A `ProUpSharedRegTx` whose signatures are presented out of share + order is invalid. +16. A `ProUpSharedRegTx` signature produced for another network's + `chainGenesisHash` is invalid. +17. A `ProUpSharedRegTx` that changes `pubKeyOperator` resets + `scriptOperatorPayout`, `netInfo`, and Platform identifiers and leaves the + masternode PoSe-banned until the new operator submits a valid + `ProUpServTx`. +18. A `ProUpRegTx` (type `3`) targeting a shared-collateral masternode + (`state.isSharedCollateral == true`) is invalid. + +### Dissolution + +1. A unilateral `ProDisTx` after the early period, with exactly one signature by + `shares[a].ownerKey`, penalty equal to `standardPenalty`, and exact non-actor outputs, is valid. +2. A unilateral `ProDisTx` during the early period uses `earlyPenalty` instead of `standardPenalty`. +3. A unanimous `ProDisTx` with `shares.size()` signatures in share order and zero penalty is valid. +4. A `ProDisTx` with `nVersion != 1` is invalid. +5. A unilateral `ProDisTx` whose actor output exceeds `shares[a].amount - penalty` is invalid. +6. A unilateral `ProDisTx` whose non-actor output `i` pays more or less than `shares[i].amount + + bonus[i]` is invalid. +7. A unilateral `ProDisTx` whose non-actor output `i` pays to a script other than + `shares[i].refundScript` is invalid. +8. A unilateral `ProDisTx` with an extra output (e.g. OP_RETURN) is invalid. +9. A unilateral `ProDisTx` with an extra input is invalid. +10. A unilateral `ProDisTx` whose actor output is omitted because the + fee equals `shares[a].amount - penalty` (so the actor remainder after the fee is zero) is valid, + and its output list is the non-actor outputs in share order with no actor slot. +11. A unilateral `ProDisTx` that includes an actor output paying zero + duffs to `shares[a].refundScript` is invalid; the actor output MUST + be omitted rather than paid as a zero-value output. +12. A unanimous `ProDisTx` whose actor output is omitted (because the + fee equals `shares[a].amount`) is valid; every non-actor output + pays exactly `shares[i].amount`. +13. A unanimous `ProDisTx` missing one participant signature is + invalid. +14. A `ProDisTx` with `mode` other than `0` or `1` is invalid. +15. A `ProDisTx` whose input sequence is changed after signing is invalid, + including when the sequence change would alter `nLockTime` behavior. +16. A `ProDisTx` whose `outputsHash` does not match the recomputed + transaction `outputsHash` is invalid; the signature check is not + reached. +17. A normal transaction (`nVersion < 3` or `nType == 0`) that spends + the collateral outpoint of a registered shared-collateral masternode, + including a PoSe-banned masternode that remains registered, is rejected + by mempool and by block validation. +18. A non-dissolution special transaction whose input spends the + collateral outpoint of a registered shared-collateral masternode is + rejected. +19. A normal (non-`ProDisTx`) transaction later in the same block as a + v4 ProRegTx with `isSharedCollateral == true` that spends the + just-created shared collateral output is rejected at block connection. +20. A unilateral `ProDisTx` for the same `proTxHash` in the same block + as the v4 ProRegTx with `isSharedCollateral == true` that creates + its shared collateral output is invalid, regardless of ordering + within the block and regardless of whether every other covenant + rule holds. +21. A unanimous `ProDisTx` for the same `proTxHash` in the same block + as the v4 ProRegTx with `isSharedCollateral == true` that creates + its shared collateral output is invalid on the same basis as the + unilateral case. +22. A `ProDisTx` (unilateral or unanimous) that spends the shared + collateral outpoint of a shared-collateral masternode whose + registering v4 ProRegTx with `isSharedCollateral == true` was + confirmed in a strictly earlier block, and that otherwise + satisfies every covenant rule, is valid; the masternode is + removed when this block is connected. +23. An ordinary transaction whose input spends an unrelated UTXO that + happens to pay `SHARED_COLLATERAL_SCRIPT` but is NOT a recorded + registered shared collateral outpoint and is NOT a same-block pending + shared collateral outpoint is NOT rejected by the covenant; + whether it succeeds depends only on ordinary script evaluation of + the underlying redeem script. + +### Reorg + +1. Disconnecting a block containing a v4 ProRegTx with `isSharedCollateral == true` fully removes + the masternode entry, including all share state. +2. Disconnecting a block containing a `ProUpShareTx` restores the prior `rewardScript`. +3. Disconnecting a block containing a `ProDisTx` restores the shared-collateral masternode entry + with its full share state. +4. Disconnecting and reconnecting a chain segment that contains a `ProUpSharedRegTx` followed later + by a `ProDisTx` in the original chain order produces the same final state. + +## Implementation Notes + +These notes are non-normative guidance for implementers. + +* The deterministic masternode state-diff bitfield must reserve a new bit for the share vector. + Implementations should follow the bit-allocation conventions already used in + `CDeterministicMNStateDiff` so that non-shared snapshots continue to round-trip exactly. +* `CheckSpecialTx` should treat shared collateral outputs as a separate spend-rejection rule rather + than as part of the per-payload checks; the spend rejection applies to *every* transaction in + mempool and block contexts, not only to special transactions. +* The same-block index of new shared collateral outputs introduced by v4 ProRegTx with + `isSharedCollateral == true` earlier in the block can be implemented as a small + `std::unordered_set` (or `std::unordered_map` if + the diagnostic value is wanted) that is populated as block transactions are validated and + consulted by every later transaction's input scan. Any spend of an outpoint in this set within the + same block is rejected, including by `ProDisTx`; same-block dissolution is not a legal path. +* Coinbase construction should reuse the existing payout-pipeline hook added by DIP-0026 (PR 184) + and append one output per share of every scheduled shared-collateral masternode with the computed + amount and target script. +* The wallet RPC surface should expose: a coordinator-style flow that builds an unsigned v4 ProRegTx + with `isSharedCollateral == true` given each participant's funding inputs and share parameters; + per-participant `joinSig` production over the consent digest; and a `dissolvemasternode` RPC that + builds a `ProDisTx`, computes the signed dissolution digest, and either collects the actor's + signature (unilateral) or the full set of signatures (unanimous). +* RPC output for `protx info` for a shared-collateral masternode should expose `isSharedCollateral`, + the share table, penalty parameters, and the canonical `SHARED_COLLATERAL_SCRIPT` template for + diagnostic purposes. + +## Security Considerations + +### Trust model + +A shared-collateral masternode (`state.isSharedCollateral == true`) requires only that consensus is +honest. No participant trusts any other participant with custody of funds. Specifically: + +* Collateral cannot be spent except through a valid `ProDisTx` for the matching masternode, enforced + by mempool and block consensus. +* Unilateral dissolution lets any participant exit at any time without the cooperation of any other + participant; the only cost is the configured penalty (paid to non-actors) and the transaction fee + (paid from the actor's share). +* Per-share reward outputs go to a participant-controlled script set at registration; the registrar + cannot rewrite them. +* Unanimous registrar updates require every participant's signature. + +### Operator compromise + +The operator can withhold service, but cannot redirect owner rewards, spend collateral, or alter +share state. Service withholding can cause a PoSe ban and thereby reduce or stop owner reward +accrual. Participants can mitigate this by replacing the operator via `ProUpSharedRegTx` (unanimous) +or by dissolving the masternode. Participants choosing an operator SHOULD treat operator selection +as a unanimous-consent decision. + +### One-participant compromise + +If one participant's owner key is compromised, the attacker can: + +* update that participant's `rewardScript` to a controlled script, redirecting only that + participant's future rewards; +* sign a unilateral `ProDisTx`, exiting the masternode with the compromised participant's share + minus the penalty (paid to the non-actor honest participants) and the transaction fee. + +The attacker cannot: + +* redirect any other participant's reward share; +* redirect any participant's refund on exit (refund scripts are immutable); +* spend collateral except through a valid dissolution; +* alter `keyIDVoting`, `pubKeyOperator`, or `nOperatorReward` (those require unanimous consent). + +This bounds the loss from a single compromised key to that participant's share plus the loss of +future reward share for the remaining lifetime of the masternode. + +### Penalty griefing + +If `earlyPenalty` and `standardPenalty` are both zero, a malicious participant can dissolve the +masternode immediately after registration at no cost to themselves, returning the other +participants' principal but disrupting service. Participants SHOULD agree on non-trivial penalty +values that compensate for the cost of redeploying the masternode. Wallets SHOULD warn when a +participant signs a registration with zero-valued penalties. + +### Future-block reward dust + +The block reward declines over time. A future block reward could be small enough that the per-share +coinbase output for the smallest share falls below policy dust thresholds. `SHARED_MIN_SHARE_DUFFS` +is chosen to keep per-share rewards safely above current dust thresholds at present reward levels; +it does not guarantee that the smallest share's reward output will remain non-dust forever. Wallets +SHOULD warn during registration when any share is small enough that anticipated future rewards could +approach dust. + +### Light client guarantees + +Shared-collateral metadata is not committed to by SML hashes. SPV clients cannot verify +shared-collateral share state without a full node or a DIP-0004-extending future proof. SPV-level +features that depend on shared-collateral metadata (filter matching of refund and reward scripts, +for example) require the full node serving the client to be honest about shared-collateral state. + +### Replay across chains + +Both the registration consent digest and the dissolution authorization digest commit explicitly to +`chainGenesisHash`, the genesis block hash of the network on which the transaction is being +authorized. The consensus digest domain therefore separates networks with different genesis hashes: +a shared-collateral registration or dissolution signed against one `chainGenesisHash` cannot be +replayed onto a network with a different genesis hash because the digest verified by consensus on +the target network would differ. + +This does not provide replay separation between forks or deployments that share the same genesis +block. Signers MUST verify the intended network / chain context before producing any `joinSig` or +dissolution signature, and implementations deploying shared-collateral mode on same-genesis forks +MUST add any additional fork-specific replay protection they require. Relying on `proTxHash` alone +is insufficient for cross-chain replay protection because matching transaction and state context +across networks cannot be ruled out by digest construction alone. + +### Mode-confusion across the discriminator + +Because the v4 ProRegTx payload version is shared between non-shared (DIP-0026) and +shared-collateral modes, an incorrectly implemented validator that ignores `isSharedCollateral` +could mis-parse one variant as the other. The registration consent digest commits to +`payload.isSharedCollateral`, so a `joinSig` produced under one variant does not verify under the +other; the deterministic masternode state carries `isSharedCollateral` as a first-class field so +that all downstream authorization, reward, and covenant rules gate on the same flag a participant +signed against. Implementations MUST treat `isSharedCollateral` as a payload-shape selector that +strictly determines which fields are deserialized and which validation path runs; falling back to a +"best effort" decode that tries both variants is invalid. + +## Open Issues + +The following implementation details are deferred and MUST be resolved before activation: + +1. **Final value of `SHARED_MIN_SHARE_DUFFS`.** Currently `1000000000` (10 DASH). Subject to + dust-policy review and feedback from wallet implementers. +2. **Activation deployment name.** Subject to release engineering confirmation that no candidate + fork bit has already been consumed. +3. **DIP-0026 v4 discriminator reservation.** DIP-0026 v4 MUST reserve the `isSharedCollateral` + discriminator byte immediately after `nMode` for every v4 ProRegTx payload, including non-shared + multi-payout registrations, before this DIP can activate. Without that reservation, shared and + non-shared v4 parsers would frame the same bytes differently. +4. **State-diff bit value for the share vector.** The exact bit position for the shared-collateral + share-vector full-replacement diff MUST be assigned before activation. + +The following protocol-level extensions are out of scope for this DIP and may be addressed by future +DIPs: + +1. External shared collateral. +2. Participant replacement without full dissolution and re-registration. +3. Owner-key rotation, with or without a relock transaction. +4. Refund-script mutation under unanimous consent. +5. Extra fee inputs and explicit change outputs in `ProDisTx`. +6. Protocol-level fractional governance among participants. + +## 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..918c1ea2 100644 --- a/project-words.txt +++ b/project-words.txt @@ -120,6 +120,8 @@ lowtopup nocredits nouser thresholdreached +infinitedash +johndoe # Names Akimov @@ -162,4 +164,12 @@ Udjin Udjinm Virgile Westrich -Wray \ No newline at end of file +Wray +ANYONECANPAY +dissolvemasternode +Serv +SIGHASH +sighash +vout + +standardness