###### tags: `zip` `zip-0` `dispute` # ZIP-0: Dispute System Overhaul :::info Author: Dr. Malte Kliemann Version: 1.0 (Sep-19-2022) ::: Based on discussions with several other members of Zeitgeist. Special thanks to the Node Team for some very helpful discussions. ## Introduction Zeitgeist's dispute system allows users to dispute a market's report or previous disputes. This report will summarize the weak points of this system and present a plan for an overhaul. We begin by describing how the dispute system works in the current version of the protocol. When a market's oracle submits a report, a window opens during which every user may dispute the reported outcome. This is called the _dispute period_. When the dispute period ends _without_ any user submitting a dispute, the market resolves to the outcome reported by the oracle. When submitting a dispute, the user specifies the outcome that they think is correct (the _suggestion_), and they reserve a bond (more on incentivization later). If a dispute is submitted, a new dispute period is started, during which users can challenge the previous dispute. If no dispute is opened within the dispute period, the dispute is _resolved_ by the market's _dispute mechanism_ (MDM). The dispute system is modular in the sense that all MDMs implement the same specification. Currently, three dispute mechanisms are available (although two of them are disabled): - `authorized`, which allow an authority (on-chain address) specified by the market creator to force set the outcome in case of a dispute. - `simple-disputes`, which simply assumes that the last dispute is correct when the dispute period ends. Currently disabled (and will remain so). - `court`, a jury-based court system similar to [Kleros](https://kleros.io). Currently, only a proof-of-concept implementation is available, and it differs from Kleros in some key points. It will remain disabled until fully implemented. If the MDM fails to provide a resolution, the original oracle report is used to resolve the market. For example, if the market's MDM is `authorized` and the oracle's report (token A) is disputed (token B), the authority must submit a report within the dispute period (token C). If they do this correctly, the market will resolve to the outcome reported by the authority (token C) at the end of the dispute period. If the authority fails to submit a report, the market will resolve to the outcome from the oracle's report (token A). In particular, note that it's possible for the market to resolve to an outcome that was not suggested by any disputant. Starting disputes is disincentivized by asking disputants to deposit a bond which is withdrawn if their suggestion was not correct and distributed amongst those disputants whose suggestion turned out to be correct. If no disputant suggested the correct outcome, the bonds are all slashed. The bonds also increase with each dispute. The current formula is $$ \texttt{next_bond} = \texttt{base} + \texttt{dispute_count} \cdot \texttt{number_of_previous_disputes}. $$ The maximum allowed number of disputes can be configured using on-chain governance. When that point is reached, the users can no longer dispute and must hope that the MDM provides the correct answer. There is a solution in the works which delegates the decision to a token holder vote as a last resort (this called a _global dispute_), but it is not yet implemented in the protocol. The PR may be found here: [zeitgeist#682](https://github.com/zeitgeistpm/zeitgeist/pull/682). ## Issues with the current protocol One common theme of the issues presented hereafter is that the dispute system contains too much and the MDMs too little logic. The specification is putting the MDMs in a straitjacket, leading to a lack of configurability, confusing code and a very clunky user experience. Let's break it down. ### Users can open disputes immediately following the last dispute If a user opens a dispute, any user of the network may immediately open another dispute on the same market. This raises some questions: - If the MDM is `authorized`, what does the second dispute accomplish? At this point, the decision is up to the authority, and no further disputes will change that. - If the MDM is `court`, then what if the court has not yet come to a conclusion? This dispute, as well, would have no use to anyone and will only cause delays. See also [zeitgeist#372](https://github.com/zeitgeistpm/zeitgeist/issues/372). ### Disputes can be opened after the authority or jury has submitted a report Even after the authority has submitted their report, it is still possible for people to open new disputes, whose main effect is resetting the dispute period timer, needlessly delaying the resolution of the market. To demonstrate a particularly ridiculous effect this has: Suppose Alice has opened a dispute suggesting outcome A, and the authority reports B. If the market now resolves, the bond that Alice reserved for making the dispute gets slashed. But if Bob opens a dispute after Alice's dispute and suggests outcome B, then Alice's bond is transferred to Bob when the market resolves (instead of getting slashed). The new dispute serves no purpose except to obtain Alice's bond that would otherwise have been slashed. Note that Alice and Bob might even be the same person using different accounts to recover their staked tokens after failing to dispute a market. Users should not be rewarded for this type of behavior. :::info This cannot be done on the current runtime configuration, which already sets `MaxDisputes` to `1`. ::: ### Configuration and design of the dispute system doesn't fit all MDMs We are forced to configure the length of the dispute period to be at least as long as however long the slowest MDM requires to provide a resolution. How do we know what's right here? Will one configuration fit all possible circumstances? What about a week? As the length of the dispute period holds for all MDMs, it now takes a week (at least!) for markets with `authorized` to resolve, even if the authority submits its report on the first day. Why doesn't the market resolve immediately after receiving the authority's report? If the MDM is `court`, and the court takes almost all of the dispute period to come to a conclusion, then almost no time remains for users to study the conclusion and open new disputes if they are not satisfied. Wouldn't you expect the dispute period to only start _after_ the jury has submitted their verdict? It is clear that `court`, for example, requires a system for allowing appeals to be made. `authorized`, on the other hand, does not. But this type of behavior is not part of the specification of MDMs, so MDMs cannot define for themselves if they need appeals. ### Dispute bonds increase only linearly Suppose two users keep re-disputing each other on a particular market. The bond they have to reserve for each dispute increases only linearly, which means that the amount needed to open the next dispute is small compared to the amount that both users have already "invested" in the dispute. Given that the winner of the dispute will scoop the bonds of the loser, this actually incentivizes continuing with the ping-pong disputes. This is somewhat similar to how bets on later streets in Limit Poker are usually only a small fraction of the pot, incentivizing players to call even if there is a high chance that they are beat. The purpose of the dispute system is not to create this amount of friction. ### Disputes can only be opened by individuals This makes configuring the bond rather difficult. If the bond is small, the effect of disincentivization is too small to prevent rich individuals from cluttering the system with disputes. If the bond is large, then less wealthy individuals will not be able to dispute reports on markets that they participated in. Justice should not be a function of wealth. It also seems somewhat undemocratic that it is up to a single individual to deposit the bond for a much needed dispute on a market where hundreds have participated. In the worst case, this creates a stalemate where everybody is waiting for everybody else to open the dispute to avoid paying the bond themselves. ### MDMs are not externally incentivized The specification for MDMs in the current dispute system does not allow funds to flow into the MDM. Furthermore, the participants who contribute to the function of the MDM are not necessarily disputants. This turns finding a solution for the dispute in the MDM into a zero sum game. Which is a bad idea. For example, the current implementation of the court system moves funds from jurors who voted for losing outcomes to the jurors who voted for the winning outcome (the winning outcome is the one that received the plurality of votes). But no fees are received for jury duty. This means that if the vote is unanimous (which is what we hope will happen most of the time), then jurors don't profit for their hard work. If you're a juror who's out for maximum profit, you almost hope that a minority of jurors votes against you, so that you can take their coins. This seems like an unhealthy dynamic between jurors. Introducing fees into this equation, allowing _all_ jurors to profit equally in case of a unanimous decision, would fix this. ## Suggestions for Mitigation This section describes the recommendations for mitigating the problems above. ### Primary Changes The leitmotif of the previous section was the lack of flexibility in the integration of MDMs in the dispute system. The primary changes suggested by this proposal will fix this issue (and many more) by removing logic from the dispute system and allowing MDMs to instead implement their own logic, as it fits their purpose. Recall that allowing more than one dispute results in all kinds of odd interactions between the dispute system and the MDMs. We mitigate this problem by putting the MDM in control of how disputes work. As before, after a market is reported, the dispute period starts, during which users can start a dispute. But opening a dispute will work slightly differently: Since different MDMs may require different methods of opening a dispute, we make opening a dispute a function of the MDM (this won't have any effect on the UI). This means that `court` can require a disputant to deposit a bond and arbitration fees (for external incentivization). The MDM will handle whatever additional logic needs to be executed, be that selecting a jury or handling appeals. It also means that the general dispute system no longer controls how much time the MDM has to provide a resolution. The actual resolution of the dispute may take longer than the dispute period, for example. It's up to the MDM to decide. Depending on the parameters, how much time the MDM requires is a variable which cannot be known before the dispute is actually solved. For example, if the market's MDM is `authorized`, the MDM (implemented as `pallet-authorized`) will allow the authority to submit their report, but certainly not provide any functionality for appeals. If the MDM is `court`, instead, then the MDM (implemented as `pallet-court`) will handle the collection of arbitration fees, the selection of the jury, it will record the individual jurors' votes and allow for appeals, which will result in additional rounds of voting. The latter is a good example of why the time window required for the resolution cannot be constant: We don't know how many appeals `court` will require until the case is closed. When the MDM has finished its job, it can either return the resolved outcome, or (depending on how `global-disputes` will finally be implemented) specify that resolution has failed and that a global dispute must be initiated. The MDM can also specify its own incentivization scheme. Maybe the authority want to take a cut from the disputant for its work. Maybe the court will only return the bond if they think the dispute was justified. This recommendation fixes the following issues: - Users can open disputes immediately following the last dispute - Disputes can be opened after the authority or jury has submitted a report - Configuration and design of the dispute system doesn't fit all MDMs - Dispute bonds increase only linearly - MDMs are not externally incentivized ### Auxiliary Changes This is a small list of changes that should be made (preferably alongside the primary changes), but are not directly related to the overhaul. #### Immediately initiate dispute if the oracle fails to submit a report Recall that in the current version of the protocol, _any_ user can submit a report if the oracle fails to submit theirs. They do not reserve a bond. In particular, they can report an incorrect outcome _without repercussions_, making it necessary for other vigilant users to create a dispute. These disputants will not be rewarded if they win the dispute, but will be punished by having their bond slashed if they lose. This is far from ideal. The solution would be to not put the decision about the report into the hands of an arbitrary community member, but rather let the protocol _immediately_ (and automatically) dispute the market. Fees and bonds can be paid from the bond that the oracle has forfeited by not reporting in time, or from the treasury. For example: The oracle of a market with MDM `authorized` fails to submit the report in time. The authority may now submit their report instead. :::info This change is only possible if all parameters required for opening a dispute (except for the market id) are made optional. ::: #### Allow crowdfunding of disputes Opening a dispute requires a bond to be reserved. What value should this bond have? As discussed above, all possible configurations of this value will be too high for some and too low for others if it is up to single users. Instead, let's set a fairly high value (the _dispute threshold_) and allow everyone to chip in whatever they can afford. If the sum of all contributions exceeds the threshold, the funds are locked as described above and the dispute is opened. If the sum of contributions does not exceed the threshold, the disputants can unlock their funds using an extrinsic. (Implementation detail: We _do not_ automatically unlock the funds to prevent unnecessary work in the chain hooks.) This recommendation fixes the following issues: - Disputes can only be opened by individuals ## Remarks - This proposal is based on the assumption that we're dealing with a valid market. In other words, MDMs are designed to handle disputes over the validity of the oracle report. They are not supposed to resolve disputes over the validity of the market. For example, if a market is missing an outcome (like TIE in case of a soccer match), the MDM cannot solve that problem. ### Why have this modular design? You might ask why we don't just tailor-make our dispute system to serve `court` and `authorized`. The obvious arguments are: - Modular design generally leads to clearer encapsulation and, therefore, to safer design. - Modular design generally leads to better future-proofing of code. Just like we may want to add additional AMM designs in the future, we may develop new MDM designs which are useful in certain situations. ## Future Proposals - Plans for the court system will be the subject of an upcoming ZIP - ## Implementation Details The most contentious point (except for the required storage migrations) is probably how to handle communication between the MDMs and the general dispute logic. The pallet `prediction-markets` is not a dependency of the MDM by design, nor does `DisputeApi` have any control over how markets are resolved, so allowing MDMs to take a variable amount of time to resolve the dispute poses a problem. Two solutions come to mind for this problem. The first is more in line with what we've been doing; the latter might have advantages in terms of software design. ### First approach: `dispute-commons` The first approach is to abstract the storage used to handle disputes into a new pallet `dispute-commons`, similar to how `market-commons` contains storage relating to markets. The new pallet will contain storage items like `MarketIdsPerDisputeBlock`. We then make `dispute-commons` a dependency of `prediction-markets`, and of each pallet that implements an MDM (`authorized`, etc.). The result is that MDMs can write to the dispute storage, which is then read by `prediction-markets` to execute the actual resolution. ![](https://i.imgur.com/tipn3rb.png) The diagram is a sketch of the architecture. The users will interact with the MDM by using the `Pallet` interface provided by each specific implementation. Since the MDMs can be quite different in design, the structure of this interface is completely up to the MDM. However, this seems to result in one serious design problem: If every MDM has full access to something like `MarketIdsPerDisputeBlock`, then they could potentially overwrite each others' data. Which means that an internal bug in one MDM might result in a failure to resolve for a market with an entirely different MDM. (This is not unlike how `swaps` can write over any market in `market-commons`, causing problems in `prediction-markets`.) Let's explain that in more detail: We need to be able to make sure each MDM can only resolve disputes for the markets that actually have the correct `market.dispute_mechanism` (until now, this is ensured by `prediction-markets`, which calls the methods of `DisputeApi` after matching `market.dispute_mechanism`). If we just give each MDM write access to `MarketIdsPerDisputeBlock`, we cannot ensure that. For example, every time a dispute for a `market` is submitted to `authorized`, it will have to verify that `market.dispute_mechanism` is equal to `MarketDisputeMechanism::Authorized(_)`. This is error-prone and leads to code duplication. It also _feels_ wrong. It's like implementing business logic in the client instead of the server. This solution must certainly be appreciated for its simplicity, but it's not ideal in terms of encapsulation. ### Second approach: pallet `disputes` Similar to the first approach, we move the _entire_ dispute logic from `prediction-markets` to a new pallet, `disputes`. But instead of just providing other pallets with access to storage items like `MarketIdsPerDisputeBlock`, the new pallet instead _controls_ the MDM using the `DisputeMechanismApi`, which each MDM must implement in addition to its implementation-specific features. So instead of making the new pallet a dependency of each MDM, the MDMs are dependencies of `disputes`. The `DisputesApi` provides the data that `disputes` holds to `prediction-markets` (and possibly other pallets, as well), so it serves the same purpose as the `DisputeCommonsApi` in the previous section. ![](https://i.imgur.com/LgJVPAP.png) The key difference is that while `dispute-commons` exposes storage like `MarketIdsPerDisputeBlock` for other pallets to write on, `disputes` encapsulates storage. The MDMs are `disputes`-agnostic. Let's flesh this out a little further: ```rust #[derive(...)] enum DisputeStatus { Resolved(OutcomeReport), Failed, } pub trait MarketDisputeMechanismApi { type MarketId; fn take_closed_disputes() -> Vec<(Self::MarketId, DisputeStatus)>; // --- snip! --- }; ``` The pallet `disputes` uses `take_closed_disputes` to query the state of disputes in MDMs. If the status is `DisputeStatus::Failed`, it delegates the dispute to `global-disputes`. ```rust pub trait DisputesApi { type MarketId; fn take_resolved_disputes() -> Vec<(Self::MarketId, OutcomeReport)>; // --- snip! --- }; ``` `prediction-markets` will use `take_resolved_disputes()` in `on_initialize` to handle the resolution of these markets. The implementation of `take_resolved_disputes` might consist of `disputes` calling `take_closed_disputes` for each MDM, making it slightly clunky. Certainly storage usage will be higher than in the first solution. Disputes are opened by calling a dispatchable in the corresponding MDM. As noted before, the MDM _shouldn't have the responsibility_ of verifying if the dispute is valid in general (i.e. ensure that the market is actually closed, and that the market actually has the right `dispute_mechanism`). In theory, the MDM could run a dispute of a market where it has no jurisdiction. The `disputes` pallet will make sure this dispute is not applied to the actual resolution of the market. Of course, in practice, the MDM _should_ nevertheless verify that the dispute is valid out of courtesy to the user. It can do so using the `market-commons` pallet.