Skip to content

Commit

Permalink
Allow MDMs to control future resolutions (#862)
Browse files Browse the repository at this point in the history
* use future resolution inside pm-pallet

* wip

* wip

* prepare storage migration

* add UpdateMarketIdsPerDisputeBlock migration

* wip

* avoid in place migration

* default to oracle report for authorized

* wip

* wip

* change test

* fix benchmark

* add resolve_failed_mdm benchmarks

* correct benchmarks

* fix nightly clippy

* fix after merge

* fix admin for Authorized

* fix global disputes

* add migration tests

* delete 0.3.7 storage migration

* cargo fmt

* modify storage versions to test try-runtime

* edit doc comment

* simplify doc comment

* update benchmark authorize outcome

* avoid indentation

* rename AuthorityReportPeriod to ReportPeriod

* use ensure

* check benchmark

* remove where clauses

* Apply suggestions from code review

Co-authored-by: Malte Kliemann <[email protected]>

* add empty commit

* use Weight instead of u64

* resolve doc comment

* rename MarketCommonsAuthorized to MarketCommons

* fmt

* allow only one dispute for authorized

* clippy

* fix benchmarks after one dispute limit authorized

* fix clippy

* add try-runtime tests

* correct benchmarks

* down tracing-core to get try-runtime logs

* rename failed to expired

* rename resolution to resolve_at

* fix small nitpicks

* edit doc comment

* use reporting period from last dispute

* modify doc string

* document private authorozed api

* fix after merge

* print market id for try-runtime tests

* bump migration versions

* avoid storage_iter

* add warning

* set block number non-zero in migration test

* move storage migration

* recalc migration markets counter

* abstract remove auto resolve

* fmt

* remove TODO

* add expired report period check

* rename to has_failed

* satisfy clippy

* check dispute resolution blocks

* use mock storage with unsafe

* use MarketIdsPerDisputeBlock inside mock

* prepare authority account id

* correct benchmarks

* add mock storage

* correct start_global_disputes benchmark

* improve migration

* remove fallible authority feature

* remove fallible authority feature

* avoid option storage query, handle 432 market

* Update zrml/authorized/src/mock_storage.rs

Co-authored-by: Malte Kliemann <[email protected]>

* fix resolve_failed_mdm bug

* Update zrml/prediction-markets/src/tests.rs

Co-authored-by: Malte Kliemann <[email protected]>

* fixes after merge

* avoid refreshing correction period

* add description for the changes made

* remove resolve_failed_mdm

* use ord_parameter_types macro

* check auto resolve in case of existing report

* handle market 432

* correct pm disputes authorized benchmarks

* fix clippy

* update pm weights

* remove unused error

* test only one dispute error

* remove unused error

* remove unused event

* cargo fmt

Co-authored-by: Malte Kliemann <[email protected]>
  • Loading branch information
Chralt98 and maltekliemann authored Jan 10, 2023
1 parent 85cefa6 commit e991d9e
Show file tree
Hide file tree
Showing 25 changed files with 1,454 additions and 601 deletions.
6 changes: 6 additions & 0 deletions docs/changelog_for_devs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
which has three fields: `who` (the account that reserved the bond), `value`
(the amount reserved), `is_settled` (a flag which determines if the bond was
already unreserved and/or (partially) slashed).
- The market dispute mechanisms are now able to control their resolution. The
`CorrectionPeriod` parameter determines how long the authorized pallet can
call `authorize_market_outcome` again after the first call to it (fat-finger
protection). The authority report now includes its resolution block number.
This is the time of the first call to `authorize_market_outcome` plus the
`CorrectionPeriod`.

# v0.3.7

Expand Down
1 change: 1 addition & 0 deletions primitives/src/constants/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use orml_traits::parameter_type_with_key;
// Authorized
parameter_types! {
pub const AuthorizedPalletId: PalletId = PalletId(*b"zge/atzd");
pub const CorrectionPeriod: BlockNumber = 4;
}

// Court
Expand Down
6 changes: 6 additions & 0 deletions primitives/src/market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@ pub struct Report<AccountId, BlockNumber> {
pub outcome: OutcomeReport,
}

#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
pub struct AuthorityReport<BlockNumber> {
pub resolve_at: BlockNumber,
pub outcome: OutcomeReport,
}

/// Contains a market id and the market period.
///
/// * `BN`: Block Number
Expand Down
2 changes: 1 addition & 1 deletion primitives/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod market_id;
mod swaps;
mod zeitgeist_multi_reservable_currency;

pub use dispute_api::DisputeApi;
pub use dispute_api::{DisputeApi, DisputeResolutionApi};
pub use market_commons_pallet_api::MarketCommonsPalletApi;
pub use market_id::MarketId;
pub use swaps::Swaps;
Expand Down
81 changes: 80 additions & 1 deletion primitives/src/traits/dispute_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// along with Zeitgeist. If not, see <https://www.gnu.org/licenses/>.

use crate::{market::MarketDispute, outcome_report::OutcomeReport, types::Market};
use frame_support::dispatch::DispatchResult;
use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight, BoundedVec};
use sp_runtime::DispatchError;

// Abstraction of the market type, which is not a part of `DisputeApi` because Rust doesn't support
Expand Down Expand Up @@ -60,4 +60,83 @@ pub trait DisputeApi {
market_id: &Self::MarketId,
market: &MarketOfDisputeApi<Self>,
) -> Result<Option<OutcomeReport>, DispatchError>;

/// Query the future resolution block of a disputed market.
/// **May** assume that `market.dispute_mechanism` refers to the calling dispute API.
///
/// # Returns
///
/// Returns the future resolution block if available, otherwise `None`.
fn get_auto_resolve(
disputes: &[MarketDispute<Self::AccountId, Self::BlockNumber>],
market_id: &Self::MarketId,
market: &MarketOfDisputeApi<Self>,
) -> Result<Option<Self::BlockNumber>, DispatchError>;

/// Returns `true` if the market dispute mechanism
/// was unable to come to a conclusion.
/// **May** assume that `market.dispute_mechanism` refers to the calling dispute API.
fn has_failed(
disputes: &[MarketDispute<Self::AccountId, Self::BlockNumber>],
market_id: &Self::MarketId,
market: &MarketOfDisputeApi<Self>,
) -> Result<bool, DispatchError>;
}

type MarketOfDisputeResolutionApi<T> = Market<
<T as DisputeResolutionApi>::AccountId,
<T as DisputeResolutionApi>::Balance,
<T as DisputeResolutionApi>::BlockNumber,
<T as DisputeResolutionApi>::Moment,
>;

pub trait DisputeResolutionApi {
type AccountId;
type Balance;
type BlockNumber;
type MarketId;
type MaxDisputes;
type Moment;

/// Resolve a market.
///
/// **Should** only be called if the market dispute
/// mechanism is ready for the resolution ([`DisputeApi::on_resolution`]).
///
/// # Returns
///
/// Returns the consumed weight.
fn resolve(
market_id: &Self::MarketId,
market: &MarketOfDisputeResolutionApi<Self>,
) -> Result<Weight, DispatchError>;

/// Add a future block resolution of a disputed market.
///
/// # Returns
///
/// Returns the number of elements in the storage structure.
fn add_auto_resolve(
market_id: &Self::MarketId,
resolve_at: Self::BlockNumber,
) -> Result<u32, DispatchError>;

/// Check if a future block resolution of a disputed market exists.
///
/// # Returns
///
/// Returns `true` if the future block resolution exists, otherwise `false`.
fn auto_resolve_exists(market_id: &Self::MarketId, resolve_at: Self::BlockNumber) -> bool;

/// Remove a future block resolution of a disputed market.
///
/// # Returns
///
/// Returns the number of elements in the storage structure.
fn remove_auto_resolve(market_id: &Self::MarketId, resolve_at: Self::BlockNumber) -> u32;

/// Get the disputes of a market.
fn get_disputes(
market_id: &Self::MarketId,
) -> BoundedVec<MarketDispute<Self::AccountId, Self::BlockNumber>, Self::MaxDisputes>;
}
1 change: 1 addition & 0 deletions runtime/battery-station/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub(crate) const FEES_AND_TIPS_BURN_PERCENTAGE: u32 = 0;
parameter_types! {
// Authorized
pub const AuthorizedPalletId: PalletId = AUTHORIZED_PALLET_ID;
pub const CorrectionPeriod: BlockNumber = BLOCKS_PER_DAY;

// Authority
pub const MaxAuthorities: u32 = 32;
Expand Down
16 changes: 13 additions & 3 deletions runtime/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ macro_rules! decl_common_types {
frame_system::ChainContext<Runtime>,
Runtime,
AllPalletsWithSystem,
zrml_prediction_markets::migrations::RecordBonds<Runtime>,
(
zrml_prediction_markets::migrations::RecordBonds<Runtime>,
zrml_prediction_markets::migrations::AddFieldToAuthorityReport<Runtime>,
),
>;

#[cfg(not(feature = "parachain"))]
Expand All @@ -68,7 +71,10 @@ macro_rules! decl_common_types {
frame_system::ChainContext<Runtime>,
Runtime,
AllPalletsWithSystem,
zrml_prediction_markets::migrations::RecordBonds<Runtime>,
(
zrml_prediction_markets::migrations::RecordBonds<Runtime>,
zrml_prediction_markets::migrations::AddFieldToAuthorityReport<Runtime>,
),
>;

pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
Expand Down Expand Up @@ -912,15 +918,18 @@ macro_rules! impl_config_traits {
impl parachain_info::Config for Runtime {}

impl zrml_authorized::Config for Runtime {
type AuthorizedDisputeResolutionOrigin = EnsureRootOrHalfAdvisoryCommittee;
type CorrectionPeriod = CorrectionPeriod;
type DisputeResolution = zrml_prediction_markets::Pallet<Runtime>;
type Event = Event;
type MarketCommons = MarketCommons;
type AuthorizedDisputeResolutionOrigin = EnsureRootOrHalfAdvisoryCommittee;
type PalletId = AuthorizedPalletId;
type WeightInfo = zrml_authorized::weights::WeightInfo<Runtime>;
}

impl zrml_court::Config for Runtime {
type CourtCaseDuration = CourtCaseDuration;
type DisputeResolution = zrml_prediction_markets::Pallet<Runtime>;
type Event = Event;
type MarketCommons = MarketCommons;
type PalletId = CourtPalletId;
Expand Down Expand Up @@ -1033,6 +1042,7 @@ macro_rules! impl_config_traits {
}

impl zrml_simple_disputes::Config for Runtime {
type DisputeResolution = zrml_prediction_markets::Pallet<Runtime>;
type Event = Event;
type MarketCommons = MarketCommons;
type PalletId = SimpleDisputesPalletId;
Expand Down
1 change: 1 addition & 0 deletions runtime/zeitgeist/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub(crate) const FEES_AND_TIPS_BURN_PERCENTAGE: u32 = 0;
parameter_types! {
// Authorized
pub const AuthorizedPalletId: PalletId = AUTHORIZED_PALLET_ID;
pub const CorrectionPeriod: BlockNumber = BLOCKS_PER_DAY;

// Authority
pub const MaxAuthorities: u32 = 32;
Expand Down
70 changes: 64 additions & 6 deletions zrml/authorized/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,77 @@

#[cfg(test)]
use crate::Pallet as Authorized;
use crate::{market_mock, Call, Config, Pallet};
use crate::{market_mock, AuthorizedOutcomeReports, Call, Config, Pallet};
use frame_benchmarking::benchmarks;
use frame_support::{dispatch::UnfilteredDispatchable, traits::EnsureOrigin};
use zeitgeist_primitives::types::OutcomeReport;
use frame_support::{
dispatch::UnfilteredDispatchable,
traits::{EnsureOrigin, Get},
};
use sp_runtime::traits::Saturating;
use zeitgeist_primitives::{
traits::DisputeResolutionApi,
types::{AuthorityReport, OutcomeReport},
};
use zrml_market_commons::MarketCommonsPalletApi;

benchmarks! {
authorize_market_outcome {
authorize_market_outcome_first_report {
let m in 1..63;

let origin = T::AuthorizedDisputeResolutionOrigin::successful_origin();
let market_id = 0u32.into();
let market = market_mock::<T>();
T::MarketCommons::push_market(market).unwrap();

frame_system::Pallet::<T>::set_block_number(42u32.into());
let now = frame_system::Pallet::<T>::block_number();
let correction_period_ends_at = now.saturating_add(T::CorrectionPeriod::get());
for _ in 1..=m {
let id = T::MarketCommons::push_market(market_mock::<T>()).unwrap();
T::DisputeResolution::add_auto_resolve(&id, correction_period_ends_at).unwrap();
}

let call = Call::<T>::authorize_market_outcome {
market_id,
outcome: OutcomeReport::Scalar(1),
};
}: {
call.dispatch_bypass_filter(origin)?
} verify {
let report = AuthorityReport {
resolve_at: correction_period_ends_at,
outcome: OutcomeReport::Scalar(1)
};
assert_eq!(AuthorizedOutcomeReports::<T>::get(market_id).unwrap(), report);
}

authorize_market_outcome_existing_report {
let origin = T::AuthorizedDisputeResolutionOrigin::successful_origin();
let market_id = 0u32.into();
let market = market_mock::<T>();
T::MarketCommons::push_market(market).unwrap();
let call = Call::<T>::authorize_market_outcome { market_id: 0_u32.into(), outcome: OutcomeReport::Scalar(1) };
}: { call.dispatch_bypass_filter(origin)? }

frame_system::Pallet::<T>::set_block_number(42u32.into());

let now = frame_system::Pallet::<T>::block_number();
let resolve_at = now.saturating_add(T::CorrectionPeriod::get());

let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(0) };
AuthorizedOutcomeReports::<T>::insert(market_id, report);

let now = frame_system::Pallet::<T>::block_number();
frame_system::Pallet::<T>::set_block_number(now + 42u32.into());

let call = Call::<T>::authorize_market_outcome {
market_id,
outcome: OutcomeReport::Scalar(1),
};
}: {
call.dispatch_bypass_filter(origin)?
} verify {
let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(1) };
assert_eq!(AuthorizedOutcomeReports::<T>::get(market_id).unwrap(), report);
}

impl_benchmark_test_suite!(
Authorized,
Expand Down
Loading

0 comments on commit e991d9e

Please sign in to comment.