From eeceba39bea89f1a10d65ff5f4449072b0311cab Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 19 Jul 2021 04:41:30 -0300 Subject: [PATCH] Third iteration of the Court pallet (#225) --- primitives/src/dispute_api.rs | 22 +++++++ primitives/src/lib.rs | 2 + .../src/resolution_counters.rs | 0 primitives/src/traits.rs | 3 +- primitives/src/types.rs | 2 +- zrml/court/src/juror.rs | 2 +- zrml/court/src/juror_status.rs | 2 +- zrml/court/src/lib.rs | 61 ++++++++++++++++--- zrml/court/src/mock.rs | 7 ++- zrml/court/src/tests.rs | 41 ++++++++++++- .../fuzz/pm_full_workflow.rs | 6 +- zrml/prediction-markets/src/benchmarks.rs | 3 +- zrml/prediction-markets/src/lib.rs | 8 +-- zrml/prediction-markets/src/migrations.rs | 2 +- zrml/prediction-markets/src/tests.rs | 3 +- zrml/simple-disputes/src/lib.rs | 58 ++++++++++-------- .../src/simple_disputes_pallet_api.rs | 27 ++------ zrml/simple-disputes/src/tests.rs | 7 ++- 18 files changed, 180 insertions(+), 76 deletions(-) create mode 100644 primitives/src/dispute_api.rs rename {zrml/simple-disputes => primitives}/src/resolution_counters.rs (100%) diff --git a/primitives/src/dispute_api.rs b/primitives/src/dispute_api.rs new file mode 100644 index 000000000..282313c72 --- /dev/null +++ b/primitives/src/dispute_api.rs @@ -0,0 +1,22 @@ +use crate::types::{Market, OutcomeReport, ResolutionCounters}; +use frame_support::dispatch::{DispatchError, DispatchResult}; + +/// Dispute Api +pub trait DisputeApi { + type AccountId; + type BlockNumber; + type MarketId; + type Origin; + + /// Disputes a reported outcome. + fn on_dispute( + origin: Self::Origin, + market_id: Self::MarketId, + outcome: OutcomeReport, + ) -> Result<[u32; 2], DispatchError>; + + /// Manages markets resolutions moving all reported markets to resolved. + fn on_resolution(now: Self::BlockNumber, cb: F) -> DispatchResult + where + F: FnMut(&Market, ResolutionCounters); +} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 09fb7a067..f129d07be 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -4,10 +4,12 @@ extern crate alloc; mod asset; pub mod constants; +mod dispute_api; mod market; mod outcome_report; mod pool; mod pool_status; +mod resolution_counters; mod serde_wrapper; mod swaps; pub mod traits; diff --git a/zrml/simple-disputes/src/resolution_counters.rs b/primitives/src/resolution_counters.rs similarity index 100% rename from zrml/simple-disputes/src/resolution_counters.rs rename to primitives/src/resolution_counters.rs diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index f6f8c3218..c47768c11 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -1,3 +1,4 @@ pub use crate::{ - swaps::Swaps, zeitgeist_multi_reservable_currency::ZeitgeistMultiReservableCurrency, + dispute_api::DisputeApi, swaps::Swaps, + zeitgeist_multi_reservable_currency::ZeitgeistMultiReservableCurrency, }; diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 839c0f94d..606836a11 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -1,6 +1,6 @@ pub use crate::{ asset::*, market::*, outcome_report::OutcomeReport, pool::Pool, pool_status::PoolStatus, - serde_wrapper::*, + resolution_counters::ResolutionCounters, serde_wrapper::*, }; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Result, Unstructured}; diff --git a/zrml/court/src/juror.rs b/zrml/court/src/juror.rs index 5e323d6f7..0af1a9bbb 100644 --- a/zrml/court/src/juror.rs +++ b/zrml/court/src/juror.rs @@ -3,7 +3,7 @@ use crate::JurorStatus; /// * Types /// /// * `B`: Balance -#[derive(parity_scale_codec::Decode, parity_scale_codec::Encode)] +#[derive(Debug, PartialEq, parity_scale_codec::Decode, parity_scale_codec::Encode)] pub struct Juror { pub(crate) staked: B, pub(crate) status: JurorStatus, diff --git a/zrml/court/src/juror_status.rs b/zrml/court/src/juror_status.rs index 66ab709dd..9a05f4bfd 100644 --- a/zrml/court/src/juror_status.rs +++ b/zrml/court/src/juror_status.rs @@ -1,4 +1,4 @@ -#[derive(parity_scale_codec::Decode, parity_scale_codec::Encode)] +#[derive(Debug, PartialEq, parity_scale_codec::Decode, parity_scale_codec::Encode)] pub enum JurorStatus { Ok, Tardy, diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index f4577e651..1ce13a0d2 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -20,38 +20,48 @@ mod pallet { use frame_support::{ dispatch::DispatchResult, pallet_prelude::StorageMap, - traits::{Currency, Hooks, IsType}, + traits::{Currency, Get, Hooks, IsType, ReservableCurrency}, Blake2_128Concat, }; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; - use sp_runtime::DispatchError; + use sp_runtime::{traits::Saturating, ArithmeticError, DispatchError, SaturatedConversion}; + use zeitgeist_primitives::{ + traits::DisputeApi, + types::{Market, OutcomeReport, ResolutionCounters}, + }; use zrml_market_commons::MarketCommonsPalletApi; pub(crate) type BalanceOf = as Currency<::AccountId>>::Balance; pub(crate) type CurrencyOf = <::MarketCommons as MarketCommonsPalletApi>::Currency; + pub(crate) type MarketIdOf = + <::MarketCommons as MarketCommonsPalletApi>::MarketId; #[pallet::call] impl Pallet { + #[frame_support::transactional] #[pallet::weight(0)] pub fn exit_court(origin: OriginFor) -> DispatchResult { let account_id = ensure_signed(origin)?; - let _ = Self::juror(&account_id)?; - Jurors::::remove(account_id); + let juror = Self::juror(&account_id)?; + Jurors::::remove(&account_id); + CurrencyOf::::unreserve(&account_id, juror.staked); Ok(()) } + #[frame_support::transactional] #[pallet::weight(0)] pub fn join_court(origin: OriginFor) -> DispatchResult { let account_id = ensure_signed(origin)?; if Jurors::::get(&account_id).is_some() { return Err(Error::::JurorAlreadyExists.into()); } - Jurors::::insert( - account_id, - Juror { staked: Default::default(), status: JurorStatus::Ok }, - ); + let jurors_num = Jurors::::iter().count(); + let jurors_num_plus_one = jurors_num.checked_add(1).ok_or(ArithmeticError::Overflow)?; + let stake = Self::current_required_stake(jurors_num_plus_one); + Jurors::::insert(&account_id, Juror { staked: stake, status: JurorStatus::Ok }); + CurrencyOf::::reserve(&account_id, stake)?; Ok(()) } } @@ -66,6 +76,9 @@ mod pallet { AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, >; + + /// Weight used to calculate the necessary staking amount to become a juror + type StakeWeight: Get>; } #[pallet::error] @@ -91,12 +104,44 @@ mod pallet { where T: Config, { + // No-one can stake more than BalanceOf::::max(), therefore, this function saturates + // arithmetic operations. + fn current_required_stake(jurors_num: usize) -> BalanceOf { + let jurors_len: BalanceOf = jurors_num.saturated_into(); + T::StakeWeight::get().saturating_mul(jurors_len) + } + // Retrieves a juror from the storage fn juror(account_id: &T::AccountId) -> Result>, DispatchError> { Jurors::::get(account_id).ok_or_else(|| Error::::JurorDoesNotExists.into()) } } + impl DisputeApi for Pallet + where + T: Config, + { + type AccountId = T::AccountId; + type BlockNumber = T::BlockNumber; + type Origin = T::Origin; + type MarketId = MarketIdOf; + + fn on_dispute( + _origin: Self::Origin, + _market_id: Self::MarketId, + _outcome: OutcomeReport, + ) -> Result<[u32; 2], DispatchError> { + todo!() + } + + fn on_resolution(_now: Self::BlockNumber, _cb: F) -> DispatchResult + where + F: FnMut(&Market, ResolutionCounters), + { + todo!() + } + } + /// Accounts that stake funds to decide outcomes. #[pallet::storage] pub type Jurors = StorageMap<_, Blake2_128Concat, T::AccountId, Juror>>; diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 520d50bc5..ca06f306a 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -15,13 +15,15 @@ use zeitgeist_primitives::{ }; pub const ALICE: AccountIdTest = 0; +pub const BOB: AccountIdTest = 1; type Block = BlockTest; type UncheckedExtrinsic = UncheckedExtrinsicTest; parameter_types! { - pub const LmPalletId: PalletId = PalletId(*b"test/lmg"); pub const BlockHashCount: u64 = BLOCK_HASH_COUNT; + pub const LmPalletId: PalletId = PalletId(*b"test/lmg"); + pub const StakeWeight: u128 = 2 * BASE; } construct_runtime!( @@ -40,6 +42,7 @@ construct_runtime!( impl crate::Config for Runtime { type Event = (); + type StakeWeight = StakeWeight; type MarketCommons = MarketCommons; } @@ -92,7 +95,7 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { - Self { balances: vec![(ALICE, 1_000 * BASE)] } + Self { balances: vec![(ALICE, 1_000 * BASE), (BOB, 1_000 * BASE)] } } } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a0ed194be..4f63bbb14 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1,10 +1,21 @@ #![cfg(test)] use crate::{ - mock::{Court, ExtBuilder, Origin, Runtime, ALICE}, - Error, + mock::{Balances, Court, ExtBuilder, Origin, Runtime, ALICE, BOB}, + Error, Juror, JurorStatus, Jurors, }; use frame_support::{assert_noop, assert_ok}; +use zeitgeist_primitives::constants::BASE; + +#[test] +fn exit_court_successfully_removes_a_juror() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Court::join_court(Origin::signed(ALICE))); + assert_eq!(Jurors::::iter().count(), 1); + assert_ok!(Court::exit_court(Origin::signed(ALICE))); + assert_eq!(Jurors::::iter().count(), 0); + }); +} #[test] fn exit_court_will_not_remove_an_unknown_juror() { @@ -16,6 +27,32 @@ fn exit_court_will_not_remove_an_unknown_juror() { }); } +#[test] +fn join_court_reserves_balance_according_to_the_number_of_jurors() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Balances::free_balance(ALICE), 1000 * BASE); + assert_ok!(Court::join_court(Origin::signed(ALICE))); + assert_eq!(Balances::free_balance(ALICE), 998 * BASE); + assert_eq!(Balances::reserved_balance(ALICE), 2 * BASE); + + assert_eq!(Balances::free_balance(BOB), 1000 * BASE); + assert_ok!(Court::join_court(Origin::signed(BOB))); + assert_eq!(Balances::free_balance(BOB), 996 * BASE); + assert_eq!(Balances::reserved_balance(BOB), 4 * BASE); + }); +} + +#[test] +fn join_court_successfully_stores_a_juror() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Court::join_court(Origin::signed(ALICE))); + assert_eq!( + Jurors::::iter().next().unwrap(), + (ALICE, Juror { staked: 2 * BASE, status: JurorStatus::Ok }) + ); + }); +} + #[test] fn join_court_will_not_insert_an_already_stored_juror() { ExtBuilder::default().build().execute_with(|| { diff --git a/zrml/prediction-markets/fuzz/pm_full_workflow.rs b/zrml/prediction-markets/fuzz/pm_full_workflow.rs index 08684f34b..cf38410fb 100644 --- a/zrml/prediction-markets/fuzz/pm_full_workflow.rs +++ b/zrml/prediction-markets/fuzz/pm_full_workflow.rs @@ -3,11 +3,13 @@ use arbitrary::Arbitrary; use frame_support::traits::Hooks; use libfuzzer_sys::fuzz_target; -use zeitgeist_primitives::types::{MarketCreation, MarketEnd, MultiHash, OutcomeReport}; +use zeitgeist_primitives::{ + traits::DisputeApi, + types::{MarketCreation, MarketEnd, MultiHash, OutcomeReport}, +}; use zrml_prediction_markets::mock::{ ExtBuilder, Origin, PredictionMarkets, SimpleDisputes, System, }; -use zrml_simple_disputes::DisputeApi; fuzz_target!(|data: Data| { let mut ext = ExtBuilder::default().build(); diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 95ed87981..222728256 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -14,12 +14,13 @@ use orml_traits::MultiCurrency; use sp_runtime::traits::SaturatedConversion; use zeitgeist_primitives::{ constants::{MinLiquidity, MinWeight, BASE}, + traits::DisputeApi, types::{ Asset, MarketCreation, MarketEnd, MarketType, MultiHash, OutcomeReport, ScalarPosition, }, }; use zrml_market_commons::MarketCommonsPalletApi; -use zrml_simple_disputes::DisputeApi; +use zrml_simple_disputes::SimpleDisputesPalletApi; // Get default values for market creation. Also spawns an account with maximum // amount of native currency diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index c318039f2..b1a306462 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -85,14 +85,14 @@ mod pallet { DispatchResult, SaturatedConversion, }; use zeitgeist_primitives::{ - traits::{Swaps, ZeitgeistMultiReservableCurrency}, + traits::{DisputeApi, Swaps, ZeitgeistMultiReservableCurrency}, types::{ Asset, Market, MarketCreation, MarketEnd, MarketStatus, MarketType, MultiHash, - OutcomeReport, Report, ScalarPosition, + OutcomeReport, Report, ResolutionCounters, ScalarPosition, }, }; use zrml_market_commons::MarketCommonsPalletApi; - use zrml_simple_disputes::{DisputeApi, ResolutionCounters}; + use zrml_simple_disputes::SimpleDisputesPalletApi; pub(crate) type BalanceOf = as Currency<::AccountId>>::Balance; @@ -738,7 +738,7 @@ mod pallet { >; /// Responsable for handling disputes - type SimpleDisputes: DisputeApi< + type SimpleDisputes: SimpleDisputesPalletApi< AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, MarketId = MarketIdOf, diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index b81771b03..2a353dc52 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -9,7 +9,7 @@ pub mod _0_1_2_move_storage_to_simple_disputes_and_market_commons { }; use zeitgeist_primitives::types::{Market, MarketDispute, PoolId}; use zrml_market_commons::MarketCommonsPalletApi; - use zrml_simple_disputes::DisputeApi; + use zrml_simple_disputes::SimpleDisputesPalletApi; const DISPUTES: &[u8] = b"Disputes"; const MARKET_COUNT: &[u8] = b"MarketCount"; diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 1d1c2e3fd..b11ecc310 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -10,13 +10,14 @@ use orml_traits::MultiCurrency; use sp_runtime::traits::AccountIdConversion; use zeitgeist_primitives::{ constants::BASE, + traits::DisputeApi, types::{ Asset, Market, MarketCreation, MarketEnd, MarketStatus, MultiHash, OutcomeReport, ScalarPosition, }, }; use zrml_market_commons::MarketCommonsPalletApi; -use zrml_simple_disputes::DisputeApi; +use zrml_simple_disputes::SimpleDisputesPalletApi; fn gen_metadata(byte: u8) -> MultiHash { let mut metadata = [byte; 50]; diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index d44293a74..72f0d745b 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -7,17 +7,15 @@ extern crate alloc; mod mock; -mod resolution_counters; mod simple_disputes_pallet_api; mod tests; pub use pallet::*; -pub use resolution_counters::ResolutionCounters; -pub use simple_disputes_pallet_api::DisputeApi; +pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; #[frame_support::pallet] mod pallet { - use crate::{DisputeApi, ResolutionCounters}; + use crate::SimpleDisputesPalletApi; use alloc::{vec, vec::Vec}; use core::{cmp, marker::PhantomData}; use frame_support::{ @@ -30,9 +28,10 @@ mod pallet { use frame_system::ensure_signed; use sp_runtime::{DispatchError, SaturatedConversion}; use zeitgeist_primitives::{ - traits::{Swaps, ZeitgeistMultiReservableCurrency}, + traits::{DisputeApi, Swaps, ZeitgeistMultiReservableCurrency}, types::{ - Asset, Market, MarketDispute, MarketStatus, MarketType, OutcomeReport, ScalarPosition, + Asset, Market, MarketDispute, MarketStatus, MarketType, OutcomeReport, + ResolutionCounters, ScalarPosition, }, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -131,15 +130,10 @@ mod pallet { #[pallet::hooks] impl Hooks for Pallet {} - impl DisputeApi for Pallet + impl SimpleDisputesPalletApi for Pallet where T: Config, { - type AccountId = T::AccountId; - type BlockNumber = T::BlockNumber; - type Origin = T::Origin; - type MarketId = MarketIdOf; - // MarketIdPerDisputeBlock fn insert_market_id_per_dispute_block( @@ -320,6 +314,31 @@ mod pallet { }) } + // Migrations (Temporary) + + fn dispute( + market_id: &Self::MarketId, + ) -> Option>> { + Disputes::::get(market_id) + } + + fn insert_dispute( + market_id: Self::MarketId, + dispute: Vec>, + ) { + Disputes::::insert(market_id, dispute); + } + } + + impl DisputeApi for Pallet + where + T: Config, + { + type AccountId = T::AccountId; + type BlockNumber = T::BlockNumber; + type Origin = T::Origin; + type MarketId = MarketIdOf; + fn on_dispute( origin: Self::Origin, market_id: Self::MarketId, @@ -450,21 +469,6 @@ mod pallet { Ok(()) } - - // Migrations (Temporary) - - fn dispute( - market_id: &Self::MarketId, - ) -> Option>> { - Disputes::::get(market_id) - } - - fn insert_dispute( - market_id: Self::MarketId, - dispute: Vec>, - ) { - Disputes::::insert(market_id, dispute); - } } #[pallet::pallet] diff --git a/zrml/simple-disputes/src/simple_disputes_pallet_api.rs b/zrml/simple-disputes/src/simple_disputes_pallet_api.rs index 85fedff18..00b3f72c4 100644 --- a/zrml/simple-disputes/src/simple_disputes_pallet_api.rs +++ b/zrml/simple-disputes/src/simple_disputes_pallet_api.rs @@ -1,15 +1,12 @@ -use crate::ResolutionCounters; use alloc::vec::Vec; -use frame_support::dispatch::{DispatchError, DispatchResult}; -use zeitgeist_primitives::types::{Market, MarketDispute, OutcomeReport}; +use frame_support::dispatch::DispatchError; +use zeitgeist_primitives::{ + traits::DisputeApi, + types::{Market, MarketDispute, ResolutionCounters}, +}; /// SimpleDisputes - Pallet Api -pub trait DisputeApi { - type AccountId; - type BlockNumber; - type MarketId; - type Origin; - +pub trait SimpleDisputesPalletApi: DisputeApi { // MarketIdPerDisputeBlock /// Inserts a disputed market ids of a block into the storage @@ -65,18 +62,6 @@ pub trait DisputeApi { /// The stored maximum number of disputes fn max_disputes() -> u32; - /// Disputes a reported outcome. - fn on_dispute( - origin: Self::Origin, - market_id: Self::MarketId, - outcome: OutcomeReport, - ) -> Result<[u32; 2], DispatchError>; - - /// Manages markets resolutions moving all reported markets to resolved. - fn on_resolution(now: Self::BlockNumber, cb: F) -> DispatchResult - where - F: FnMut(&Market, ResolutionCounters); - // Migrations (Temporary) fn dispute( diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index ee9e73110..5aaf3ec57 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -1,9 +1,10 @@ #![cfg(test)] -use crate::{mock::*, DisputeApi, Error, MarketIdsPerReportBlock}; +use crate::{mock::*, Error, MarketIdsPerReportBlock, SimpleDisputesPalletApi}; use frame_support::{assert_noop, assert_ok}; -use zeitgeist_primitives::types::{ - Market, MarketCreation, MarketEnd, MarketStatus, MarketType, OutcomeReport, Report, +use zeitgeist_primitives::{ + traits::DisputeApi, + types::{Market, MarketCreation, MarketEnd, MarketStatus, MarketType, OutcomeReport, Report}, }; use zrml_market_commons::MarketCommonsPalletApi;