Skip to content

Decentralized moderation teams: masternode-elected moderators per contract (decisions and open questions) #4865

Description

@QuantumExplorer

Summary

A data contract can declare that its moderation team is chosen by masternodes and evonodes instead of by the contract owner. Teams apply by publishing a charter, masternodes elect one, and the seated team moderates the contract's content under the limits of its charter, paid from the contract's moderators fee pot. A seated team can only be replaced by a challenge vote.

All design questions are answered (rounds 1 to 8, 2026-09-19 to 2026-09-21; the round comments below hold the history). This body is the consolidated design. Nothing is built. Target: protocol version 14 on v4.2-dev, version tables edited in place.

It builds on what already merged:

PR What it gives us
#4830 Contract moderation: ContractModerators::{ContractOwner, AppointedModerators}, banlist + suspensions, ContractUserModeration state transition (type 24), actor-level gate in the batch transformer
#4849 A reason on every ban and suspension: ContractModerationReason { code: Option<u16>, text <= 1024 bytes }, the code is never checked today
#4857 Moderators delete documents of types flagged canBeDeletedByModerators, permanent removal record, storage refund forfeited
#4864 canBeDeletedByModeratorsFor: a window after a document's last modification for moderator deletes
#4851, #4856 Document action fees (fixed credits per action, immutable) into an owner pot and a moderators pot, ContractFeeClaim (type 25), equal split between team(), once per pot per epoch, last claim record, getContractFeePots
#4858 $actionFeeAgreement: a document transition states the action fee it agrees to pay, with an increase tolerance

Decisions

1. Contract-side declaration (frozen at creation)

  • D3, D7. A third ContractModerators variant, exclusive with the two merged ones. Declarable only at contract creation; once elected, never out. (feat(platform)!: contract moderation with a banlist and a suspension list #4830 already refuses enabling moderation on update.)
  • D8. Election parameters are frozen once set. Seconds as u32, bounds in SystemLimits: join window and vote window 1 day to 4 weeks, default 1 week each; challenge cool-down x 2 weeks to 3 years.
  • D25. The contract declares which document types it moderates (the moderated set). The charter never chooses types; it says how they are moderated.
  • D47. The moderated set may include types not flagged canBeDeletedByModerators. Deletes reach only the flagged types, within each type's window. Bans and suspensions are not limited by the flag.
  • D62, D64; D63 dropped (2026-09-22, round 9). The contract declares, per moderated document type, the abilities a charter may claim on it (non-empty). There are no charter fee maximums: a type's own actionFees.moderators amount is the most a team may charge on its actions, and a charter charges a share of it, so the signer's fee agreement to the declared amounts never mismatches a seated charter. Owner pot amounts stay contract-fixed and immutable.
  • D6, D17, D18. Interim mode until the first team is seated: the owner moderates, an appointed set moderates (the merged kinds, no power group), or the moderated types are not yet usable. "Not yet usable" blocks only the moderated types; if no team ever comes those types are dead forever. Accepted. A fourth interim, noModeration (2026-09-22, round 9): nobody moderates and nobody claims the pot until a team is seated, but the moderated types stay usable meanwhile.
  • D19, D26. Interim bans, suspensions and removals survive the first seating. Interim moderators may claim the pot until seating; at the first seating the pot is not settled, it carries over to the elected team.
  • D20, D27. The contract says whether the owner is protected from moderation in elected mode, default not protected. The leader and the current members are always protected (41102).

2. Charter and team

  • D15, D57, D59, D60. Charters are documents of a new system data contract. An application is a charter document create on a contested unique index keyed by the target contract id, reusing the DPNS contest machinery with the windows and the fee read from the target contract. The seated team (leader, members with powers, active charter id) lives under the moderated contract for a cheap read; the system contract holds only charters. Charter documents are immutable; an amendment is a new charter document plus a vote that swaps the active charter id.
  • D1 (amended), D58. Consensus enforces abilities, per-ability powers, the fee amounts and the split from the charter, and the document types from the contract. The "what we moderate" text is informational, capped at 4096 bytes.
  • D50. The charter lists its reason codes (1 = hate speech, 2 = doxing, ...). Every moderation action's code must be one the seated charter lists.
  • D2, D9, D28, D29, D30, D31. A team is a group with power. The applicant pays, is the contender, and becomes the leader. The initial member list with powers is part of the application and is seated as-is. The leader is a member with a power of their own, assigns powers freely, cap 16. The charter sets a required power per ability. A membership change that leaves any ability unreachable is refused.
  • D10, D11, D33. One team per contract. The leader cannot change. A challenge is the only remedy for a lost-keys or rogue leader.
  • D4. No terms, no resignation.

3. Abilities

  • D48. Silence is the merged suspension, as-is.
  • D49. Warn is an on-chain record with no consensus effect and no expiry. In scope.
  • D51. Ban and suspension scope stays contract-wide. The moderated set limits deletes and the interim block only.

4. Fees and the split

  • D45, D65. The charter sets the moderators per-action amounts within the contract's maximum; $actionFeeAgreement protects users. New amounts apply immediately at the block a seating or amendment passes; the agreement's tolerance absorbs small rises, larger ones fail at recheck and the user re-signs. Needs validate_action_fees_unchanged lifted for the moderators part and the RecheckTx gap closed. Teams compete on price.
  • D34, D38. The claim split is written in the charter, three percentages summing to 100, free within 0 to 100: a leader share, a share split equally between the other members, and a share split by each member's moderation action count. Example 10 / 40 / 50.
  • D35, D36, D37. Only charter-allowed actions count, reversals excluded, farming accepted (the leader removes a farmer). Every signer of a threshold action gets a count. Counters reset at every settle; with no actions in the period the action share is split equally.
  • D32, D13. Adding and removing a member both force a settle of the moderators pot first, ignoring the once-per-epoch limit. At a seat change between elected teams the pot is settled to the outgoing team first.

5. Initial election

  • D21, D22, D23, D43, D52, D53. New VotePoll variants on the MasternodeVote machinery. Contender poll without Lock. The applicant with the most votes wins, plurality, no minimum; with zero votes the first applicant wins. "First" is block time, then block height, then document id. A single applicant wins when the join window closes, with no vote window.
  • D24, D54, D55, D16. Applying costs 0.5 Dash, prefunding the masternode votes, the remainder released as processing fees at clean-up. The amounts are new fields of VoteResolutionFundFees in rs-platform-version, shared by application, challenge and amendment polls. No extra spam limit.
  • D56. DPNS is untouched: first-wins applies only to the new poll kinds.

6. Challenges and amendments

  • D40, D41, D42. A challenge runs in two stages. The first challenger opens it and pays; the contract's join window admits more challengers, each paying the same fee; nobody joins after it closes. Stage 1, "replace the incumbent?", runs for the vote window and passes when yes power exceeds two thirds of yes plus no (abstain excluded) and yes plus no is at least 400 (evonode 4, masternode 1). Stage 2, "which challenger?", is a contender poll with plurality and first-wins, skipped with a single challenger. The winner is seated immediately. The challengers' fees pool into one prefunded balance for both stages; nothing is refunded on failure.
  • D39, D46, D61. Challenges are allowed at most once every x, counted from the last seat change. A failed challenge starts no cool-down; instead the fee doubles for each challenge opened within 3 months of the previous challenge's close (relative to the previous fee, reset to base after 3 months, system constant, saturating). Amendments have their own clock of the same length, reset by a passed amendment, so an amendment never shields a team from a challenge.
  • D44. The leader prefunds an amendment vote. An amendment may run while a challenge is open. Per-ability powers are amendable only by vote.
  • D12. Bans and suspensions persist across a handover.

7. Scope

  • D5. Protocol version 14, v4.2-dev, tables edited in place.
  • D14. Appeals are a different PR; the design must not block them.
  • Clients: consensus, DAPI queries, proof verifier, rs-sdk and wasm/JS in the stack; Swift and Kotlin later.

Findings in the code that shape the work

  • The shipped contested-document tie-break awards the LATEST contender (check_for_ended_vote_polls v0, max_by(created_at, ...)). DPNS keeps it (D56); the new polls use min_by.
  • The contested document fund is 0.1 Dash at PV14 (VOTE_RESOLUTION_FUND_FEES_VERSION2), the single vote cost 0.0001 Dash, and a vote is refused once the prefunded balance is empty. 0.5 Dash (D54) covers 5,000 votes.
  • clean_up_after_contested_resources_vote_polls_end v1 already releases the remainder of a prefunded balance as processing credits.
  • VotePoll has a single variant. YesNoAbstainVoteChoice already exists in rs-dpp.
  • The merged claim pays equal shares to team() once per pot per epoch and leaves the remainder. D32 and D13 are forced claims that run before the membership or seat change; D34 replaces the equal split.
  • The merged moderation gate is actor-level and contract-wide, which D51 keeps. Per-type scope is needed only for deletes (already per type) and the interim block.
  • The contract's "other" tree [64, contract_id, 2] already holds keys 16, 32, 64, 96, 128, 192, and tests pin which key tops it. The seated team, the action counters and the two cool-down clocks need a home there or their own.
  • ContractModerationReason.code is stored unchecked today; D50 makes the seated charter's code list the check.

PR stack

  1. Precursor: generalise vote polls. New VotePoll variants (contender poll without Lock, yes/no poll with supermajority and minimum power), first-wins tie-break for the new kinds, the 0.5 Dash fund fields, windows and fee sourced from a target contract.
  2. Charter system data contract, plus the contract-side declaration: elected ContractModerators variant with its frozen parameters, moderated set, ability envelope, per-action maximums, interim mode, owner flag.
  3. Application, initial election, seating. Seated-team state under the contract, per-ability powers, per-type delete gate, charter-priced moderators amounts with the fee-immutability check lifted and the RecheckTx gap closed.
  4. Membership by the leader with forced settles, the three-part split, action counters, reason-code check.
  5. Challenges (two stages, escalating fee, cool-down clock) and amendments (own clock, active charter swap).
  6. Warn.
  7. DAPI queries, proof verifier, rs-sdk, wasm/JS. Mobile later. Appeals separate.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions