From 852e912b8bccf762cec62814cee58beac07a57a7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 15 Dec 2022 15:55:45 +0100 Subject: [PATCH 001/368] fix matches outcome report in global disputes --- zrml/global-disputes/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index e71ed9e76..ba22566b5 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -189,6 +189,8 @@ mod pallet { OutcomeAlreadyExists, /// The outcome specified is not present in the voting outcomes. OutcomeDoesNotExist, + /// Submitted outcome does not match market type. + OutcomeMismatch, /// The global dispute period is not over yet. The winner is not yet determined. UnfinishedGlobalDispute, /// The maximum number of votes for this account is reached. @@ -221,6 +223,9 @@ mod pallet { ) -> DispatchResultWithPostInfo { let owner = ensure_signed(origin)?; + let market = T::MarketCommons::market(&market_id)?; + ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); + let winner_info = >::get(market_id).ok_or(Error::::NoGlobalDisputeStarted)?; ensure!(!winner_info.is_finished, Error::::GlobalDisputeAlreadyFinished); From a71df3530d4ede4233e84d225fdae2f7d2b9ea06 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 15 Dec 2022 17:28:39 +0100 Subject: [PATCH 002/368] fix CI --- zrml/global-disputes/src/benchmarks.rs | 7 ++++-- zrml/global-disputes/src/lib.rs | 33 ++++++++++++++++++++++++++ zrml/global-disputes/src/tests.rs | 13 ++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index e6524e461..8026ab24f 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -25,8 +25,8 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{ - global_disputes_pallet_api::GlobalDisputesPalletApi, types::*, BalanceOf, Call, Config, - Pallet as GlobalDisputes, *, + global_disputes_pallet_api::GlobalDisputesPalletApi, market_mock, types::*, BalanceOf, Call, + Config, Pallet as GlobalDisputes, *, }; use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_support::{ @@ -38,6 +38,7 @@ use frame_system::RawOrigin; use sp_runtime::traits::{Bounded, SaturatedConversion, Saturating}; use sp_std::prelude::*; use zeitgeist_primitives::types::OutcomeReport; +use zrml_market_commons::MarketCommonsPalletApi; fn deposit(caller: &T::AccountId) where @@ -213,6 +214,8 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); let market_id: MarketIdOf = 0u128.saturated_into(); + let market = market_mock::(); + T::MarketCommons::push_market(market).unwrap(); let outcome = OutcomeReport::Scalar(20); >::insert(market_id, winner_info); deposit::(&caller); diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index ba22566b5..649640797 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -53,6 +53,8 @@ mod pallet { use zeitgeist_primitives::types::OutcomeReport; use zrml_market_commons::MarketCommonsPalletApi; + pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; + #[pallet::config] pub trait Config: frame_system::Config { /// The currency implementation used to lock tokens for voting. @@ -657,3 +659,34 @@ mod pallet { } } } + +#[cfg(any(feature = "runtime-benchmarks", test))] +pub(crate) fn market_mock() +-> zeitgeist_primitives::types::Market> +where + T: crate::Config, +{ + use frame_support::traits::Get; + use sp_runtime::traits::AccountIdConversion; + use zeitgeist_primitives::types::ScoringRule; + + zeitgeist_primitives::types::Market { + creation: zeitgeist_primitives::types::MarketCreation::Permissionless, + creator_fee: 0, + creator: T::GlobalDisputesPalletId::get().into_account_truncating(), + market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=u128::MAX), + dispute_mechanism: zeitgeist_primitives::types::MarketDisputeMechanism::SimpleDisputes, + metadata: Default::default(), + oracle: T::GlobalDisputesPalletId::get().into_account_truncating(), + period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), + deadlines: zeitgeist_primitives::types::Deadlines { + grace_period: 1_u32.into(), + oracle_duration: 1_u32.into(), + dispute_duration: 1_u32.into(), + }, + report: None, + resolved_outcome: None, + scoring_rule: ScoringRule::CPMM, + status: zeitgeist_primitives::types::MarketStatus::Disputed, + } +} diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 8c46b357d..9051f06f0 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -19,6 +19,7 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, + market_mock, mock::*, types::{OutcomeInfo, WinnerInfo}, Error, Event, Locks, MarketIdOf, Outcomes, Winners, @@ -34,6 +35,7 @@ use zeitgeist_primitives::{ constants::mock::{GlobalDisputeLockId, MinOutcomeVoteAmount, VotingOutcomeFee, BASE}, types::OutcomeReport, }; +use zrml_market_commons::Markets; const SETUP_AMOUNT: u128 = 100 * BASE; @@ -71,7 +73,10 @@ fn check_outcome_sum( #[test] fn add_vote_outcome_works() { ExtBuilder::default().build().execute_with(|| { + // create market let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); GlobalDisputes::push_voting_outcome( &market_id, OutcomeReport::Scalar(0), @@ -110,6 +115,8 @@ fn add_vote_outcome_works() { fn add_vote_outcome_fails_if_no_global_dispute_present() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); assert_noop!( GlobalDisputes::add_vote_outcome( Origin::signed(ALICE), @@ -125,6 +132,8 @@ fn add_vote_outcome_fails_if_no_global_dispute_present() { fn add_vote_outcome_fails_if_global_dispute_finished() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); let mut winner_info = WinnerInfo::new(OutcomeReport::Scalar(0), 10 * BASE); winner_info.is_finished = true; >::insert(market_id, winner_info); @@ -144,6 +153,8 @@ fn add_vote_outcome_fails_if_global_dispute_finished() { fn add_vote_outcome_fails_if_outcome_already_exists() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); GlobalDisputes::push_voting_outcome( &market_id, OutcomeReport::Scalar(0), @@ -171,6 +182,8 @@ fn add_vote_outcome_fails_if_outcome_already_exists() { fn add_vote_outcome_fails_if_balance_too_low() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); GlobalDisputes::push_voting_outcome( &market_id, OutcomeReport::Scalar(0), From fed893f0125d44fb28e8d25d4e55b5b05aeb875a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 16 Dec 2022 11:26:34 +0100 Subject: [PATCH 003/368] add tests outcome mismatch and market check --- zrml/global-disputes/src/tests.rs | 49 ++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 9051f06f0..4054d10c4 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -35,7 +35,7 @@ use zeitgeist_primitives::{ constants::mock::{GlobalDisputeLockId, MinOutcomeVoteAmount, VotingOutcomeFee, BASE}, types::OutcomeReport, }; -use zrml_market_commons::Markets; +use zrml_market_commons::{Error as MarketError, Markets}; const SETUP_AMOUNT: u128 = 100 * BASE; @@ -111,6 +111,53 @@ fn add_vote_outcome_works() { }); } +#[test] +fn add_vote_outcome_fails_with_outcome_mismatch() { + ExtBuilder::default().build().execute_with(|| { + // create scalar market + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + GlobalDisputes::push_voting_outcome( + &market_id, + OutcomeReport::Scalar(0), + &ALICE, + 10 * BASE, + ) + .unwrap(); + assert_noop!( + GlobalDisputes::add_vote_outcome( + Origin::signed(ALICE), + market_id, + OutcomeReport::Categorical(0u16), + ), + Error::::OutcomeMismatch + ); + }); +} + +#[test] +fn add_vote_outcome_fails_with_non_existing_market() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + GlobalDisputes::push_voting_outcome( + &market_id, + OutcomeReport::Scalar(0), + &ALICE, + 10 * BASE, + ) + .unwrap(); + assert_noop!( + GlobalDisputes::add_vote_outcome( + Origin::signed(ALICE), + market_id, + OutcomeReport::Scalar(20), + ), + MarketError::::MarketDoesNotExist + ); + }); +} + #[test] fn add_vote_outcome_fails_if_no_global_dispute_present() { ExtBuilder::default().build().execute_with(|| { From 69ae96cf3100bca0b4cf845dd7ce12e4ea2a3ae8 Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 19 Dec 2022 15:36:41 +0100 Subject: [PATCH 004/368] Update zrml/global-disputes/src/tests.rs Co-authored-by: Malte Kliemann --- zrml/global-disputes/src/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 4054d10c4..4b0bd61f1 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -73,7 +73,6 @@ fn check_outcome_sum( #[test] fn add_vote_outcome_works() { ExtBuilder::default().build().execute_with(|| { - // create market let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); From 8d6bcac9083d30472ea48b67acb968268cd3233f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 20 Dec 2022 11:38:07 +0100 Subject: [PATCH 005/368] remove event OutcomeOwnersRewardedWithNoFunds --- zrml/global-disputes/src/benchmarks.rs | 37 ----------------------- zrml/global-disputes/src/lib.rs | 17 +++-------- zrml/global-disputes/src/tests.rs | 42 +------------------------- zrml/global-disputes/src/weights.rs | 10 ------ 4 files changed, 5 insertions(+), 101 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index 8026ab24f..b45f6d7e0 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -287,43 +287,6 @@ benchmarks! { assert!(T::Currency::free_balance(&reward_account) == 0u128.saturated_into()); } - reward_outcome_owner_no_funds { - let o in 1..T::MaxOwners::get(); - - let market_id: MarketIdOf = 0u128.saturated_into(); - - let mut owners = Vec::new(); - for i in 1..=o { - let owner = account("winners_owner", i, 0); - owners.push(owner); - } - let owners = BoundedVec::try_from(owners.clone()).unwrap(); - - let winner_info = WinnerInfo { - outcome: OutcomeReport::Scalar(0), - is_finished: true, - outcome_info: OutcomeInfo { - outcome_sum: 42u128.saturated_into(), - owners, - }, - }; - >::insert(market_id, winner_info); - - let caller: T::AccountId = whitelisted_caller(); - - let outcome = OutcomeReport::Scalar(20); - - let reward_account = GlobalDisputes::::reward_account(&market_id); - assert!(T::Currency::free_balance(&reward_account) == 0u128.saturated_into()); - - deposit::(&caller); - }: { - >::reward_outcome_owner(RawOrigin::Signed(caller.clone()).into(), market_id) - .unwrap(); - } verify { - assert_last_event::(Event::OutcomeOwnersRewardedWithNoFunds:: { market_id }.into()); - } - purge_outcomes { // RemoveKeysLimit - 2 to ensure that we actually fully clean and return at the end let k in 1..(T::RemoveKeysLimit::get() - 2); diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 649640797..a937126ee 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -158,8 +158,6 @@ mod pallet { }, /// The winner of the global dispute system is determined. GlobalDisputeWinnerDetermined { market_id: MarketIdOf }, - /// No funds could be spent as reward to the outcome owner(s). - OutcomeOwnersRewardedWithNoFunds { market_id: MarketIdOf }, /// The outcome owner has been rewarded. OutcomeOwnersRewarded { market_id: MarketIdOf, owners: Vec> }, /// The outcomes storage item is partially cleaned. @@ -185,6 +183,8 @@ mod pallet { InsufficientAmount, /// The maximum amount of owners is reached. MaxOwnersReached, + /// The amount in the reward pot is zero. + NoFundsToReward, /// No global dispute present at the moment. NoGlobalDisputeStarted, /// The voting outcome has been already added. @@ -325,11 +325,7 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of owners for the winning outcome. #[frame_support::transactional] - #[pallet::weight( - T::WeightInfo::reward_outcome_owner_no_funds(T::MaxOwners::get()).max( - T::WeightInfo::reward_outcome_owner_with_funds(T::MaxOwners::get()), - ) - )] + #[pallet::weight(T::WeightInfo::reward_outcome_owner_with_funds(T::MaxOwners::get()))] pub fn reward_outcome_owner( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, @@ -347,14 +343,9 @@ mod pallet { let reward_account = Self::reward_account(&market_id); let reward_account_free_balance = T::Currency::free_balance(&reward_account); + ensure!(!reward_account_free_balance.is_zero(), Error::::NoFundsToReward); let owners_len = winner_info.outcome_info.owners.len() as u32; - if reward_account_free_balance.is_zero() { - Self::deposit_event(Event::OutcomeOwnersRewardedWithNoFunds { market_id }); - // return early case if there is no reward - return Ok((Some(T::WeightInfo::reward_outcome_owner_no_funds(owners_len))).into()); - } - let mut remainder = reward_account_free_balance; let owners_len_in_balance: BalanceOf = >::from(owners_len); if let Some(reward_per_each) = diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 4b0bd61f1..3e0e8e7a3 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -377,44 +377,6 @@ fn reward_outcome_owner_works_for_one_owner() { }); } -#[test] -fn reward_outcome_owner_works_for_no_reward_funds() { - ExtBuilder::default().build().execute_with(|| { - let market_id = 0u128; - - setup_vote_outcomes_with_hundred(&market_id); - - let winner_info = WinnerInfo { - outcome: OutcomeReport::Scalar(20), - is_finished: true, - outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, owners: Default::default() }, - }; - >::insert(market_id, winner_info); - - assert_ok!(GlobalDisputes::purge_outcomes(Origin::signed(ALICE), market_id,)); - - System::assert_last_event(Event::::OutcomesFullyCleaned { market_id }.into()); - - let free_balance_alice_before = Balances::free_balance(&ALICE); - - let reward_account_free_balance = - Balances::free_balance(&GlobalDisputes::reward_account(&market_id)); - // this case happens, when add_vote_outcome wasn't called - // so no loosers, who provided the VotingOutcomeFee - assert!(reward_account_free_balance.is_zero()); - - assert_ok!(GlobalDisputes::reward_outcome_owner(Origin::signed(ALICE), market_id)); - - System::assert_last_event( - Event::::OutcomeOwnersRewardedWithNoFunds { market_id }.into(), - ); - - assert_eq!(Balances::free_balance(&ALICE), free_balance_alice_before); - assert!(Balances::free_balance(GlobalDisputes::reward_account(&market_id)).is_zero()); - assert!(>::iter_prefix(market_id).next().is_none()); - }); -} - #[test] fn vote_fails_if_amount_below_min_outcome_vote_amount() { ExtBuilder::default().build().execute_with(|| { @@ -878,7 +840,7 @@ fn determine_voting_winner_works_with_accumulated_votes_for_alice() { } #[test] -fn reward_outcome_owner_cleans_outcome_info() { +fn purge_outcomes_works() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; @@ -911,8 +873,6 @@ fn reward_outcome_owner_cleans_outcome_info() { System::assert_last_event(Event::::OutcomesFullyCleaned { market_id }.into()); - assert_ok!(GlobalDisputes::reward_outcome_owner(Origin::signed(BOB), market_id,)); - assert_eq!(>::iter_prefix(market_id).next(), None); }); } diff --git a/zrml/global-disputes/src/weights.rs b/zrml/global-disputes/src/weights.rs index a549c252f..dc2aef1bb 100644 --- a/zrml/global-disputes/src/weights.rs +++ b/zrml/global-disputes/src/weights.rs @@ -50,7 +50,6 @@ pub trait WeightInfoZeitgeist { fn unlock_vote_balance_remove(l: u32, o: u32) -> Weight; fn add_vote_outcome(w: u32) -> Weight; fn reward_outcome_owner_with_funds(o: u32) -> Weight; - fn reward_outcome_owner_no_funds(o: u32) -> Weight; fn purge_outcomes(k: u32, o: u32) -> Weight; } @@ -118,15 +117,6 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(o as Weight))) } - // Storage: GlobalDisputes Outcomes (r:1 w:0) - // Storage: GlobalDisputes Winners (r:1 w:0) - // Storage: System Account (r:1 w:0) - fn reward_outcome_owner_no_funds(o: u32) -> Weight { - (39_929_000 as Weight) - // Standard Error: 3_000 - .saturating_add((13_000 as Weight).saturating_mul(o as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - } // Storage: GlobalDisputes Winners (r:1 w:1) // Storage: GlobalDisputes Outcomes (r:3 w:2) fn purge_outcomes(k: u32, _o: u32) -> Weight { From 5b6bf80d54dec00aae4a1caff17ac648603fa370 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 21 Dec 2022 08:59:16 +0100 Subject: [PATCH 006/368] prepare destruction of global dispute --- .../src/global_disputes_pallet_api.rs | 8 +- zrml/global-disputes/src/lib.rs | 137 ++++++++++++------ zrml/global-disputes/src/types.rs | 22 ++- 3 files changed, 118 insertions(+), 49 deletions(-) diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index b4a14cae7..90eafcc9e 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -18,7 +18,7 @@ extern crate alloc; use alloc::vec::Vec; -use sp_runtime::DispatchResult; +use sp_runtime::{DispatchError, DispatchResult}; use zeitgeist_primitives::types::OutcomeReport; /// The trait to initiate and resolve the global disputes. @@ -81,4 +81,10 @@ pub trait GlobalDisputesPalletApi { fn is_not_started(market_id: &MarketId) -> bool { !Self::is_started(market_id) } + + /// Destroy a global dispute and allow to return all funds of the participants. + /// + /// # Arguments + /// - `market_id` - The id of the market. + fn destroy_global_dispute(market_id: &MarketId) -> Result<(), DispatchError>; } diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index a937126ee..eaeaa2e8a 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -47,7 +47,7 @@ mod pallet { use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Saturating, Zero}, - DispatchResult, + DispatchError, DispatchResult, }; use sp_std::{vec, vec::Vec}; use zeitgeist_primitives::types::OutcomeReport; @@ -109,7 +109,7 @@ mod pallet { pub type AccountIdOf = ::AccountId; pub type OutcomeInfoOf = OutcomeInfo, OwnerInfoOf>; - pub type WinnerInfoOf = WinnerInfo, OwnerInfoOf>; + pub type GDInfoOf = GDInfo, OwnerInfoOf>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; type OwnerInfoOf = BoundedVec, ::MaxOwners>; pub type LockInfoOf = @@ -139,10 +139,11 @@ mod pallet { >; /// Maps the market id to all information - /// about the winner outcome and if the global dispute is finished. + /// about the global dispute. + // TODO what storage migration after changing the name? #[pallet::storage] - pub type Winners = - StorageMap<_, Twox64Concat, MarketIdOf, WinnerInfoOf, OptionQuery>; + pub type GlobalDisputesInfo = + StorageMap<_, Twox64Concat, MarketIdOf, GDInfoOf, OptionQuery>; #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] @@ -179,6 +180,8 @@ mod pallet { AmountTooLow, /// The global dispute period is already over and the winner is determined. GlobalDisputeAlreadyFinished, + /// The global dispute status is invalid for this operation. + InvalidGlobalDisputeStatus, /// Sender does not have enough funds for the vote on an outcome. InsufficientAmount, /// The maximum amount of owners is reached. @@ -228,9 +231,9 @@ mod pallet { let market = T::MarketCommons::market(&market_id)?; ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); - let winner_info = - >::get(market_id).ok_or(Error::::NoGlobalDisputeStarted)?; - ensure!(!winner_info.is_finished, Error::::GlobalDisputeAlreadyFinished); + let gd_info = >::get(market_id) + .ok_or(Error::::NoGlobalDisputeStarted)?; + ensure!(gd_info.status == GDStatus::Active, Error::::InvalidGlobalDisputeStatus); ensure!( !>::contains_key(market_id, &outcome), @@ -257,6 +260,47 @@ mod pallet { Ok((Some(T::WeightInfo::add_vote_outcome(0u32))).into()) } + /// Return the voting outcome fees in case the global dispute was destroyed. + /// + /// # Arguments + /// + /// - `market_id`: The id of the market. + /// + /// # Weight + /// + /// Complexity: `O(n)`, + /// where `n` is the number of all existing outcomes for a global dispute. + #[frame_support::transactional] + #[pallet::weight(5000)] + pub fn refund_vote_fees( + origin: OriginFor, + #[pallet::compact] market_id: MarketIdOf, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; + + let gd_info = >::get(market_id) + .ok_or(Error::::NoGlobalDisputeStarted)?; + ensure!(gd_info.status == GDStatus::Destroyed, Error::::UnfinishedGlobalDispute); + + let mut owners_len = 0u32; + let mut removed_keys_amount = 0u32; + for (_, outcome_info) in + >::drain_prefix(market_id).take(T::RemoveKeysLimit::get() as usize) + { + owners_len = owners_len.max(outcome_info.owners.len() as u32); + removed_keys_amount = removed_keys_amount.saturating_add(1u32); + } + + if >::iter_prefix(market_id).next().is_none() { + Self::deposit_event(Event::OutcomesFullyCleaned { market_id }); + } else { + Self::deposit_event(Event::OutcomesPartiallyCleaned { market_id }); + } + + // weight for max owners, because we don't know + Ok((Some(T::WeightInfo::purge_outcomes(removed_keys_amount, owners_len))).into()) + } + /// Purge all outcomes to allow the winning outcome owner(s) to get their reward. /// Fails if the global dispute is not concluded yet. /// @@ -279,12 +323,12 @@ mod pallet { ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - let mut winner_info = - >::get(market_id).ok_or(Error::::NoGlobalDisputeStarted)?; - ensure!(winner_info.is_finished, Error::::UnfinishedGlobalDispute); + let mut gd_info = >::get(market_id) + .ok_or(Error::::NoGlobalDisputeStarted)?; + ensure!(gd_info.status == GDStatus::Finished, Error::::UnfinishedGlobalDispute); let winning_outcome: Option> = - >::get(market_id, &winner_info.outcome); + >::get(market_id, &gd_info.outcome); let mut owners_len = 0u32; // move the winning outcome info to Winners before it gets drained if let Some(outcome_info) = winning_outcome { @@ -292,8 +336,8 @@ mod pallet { // storage write is needed here in case, // that the first call of reward_outcome_owner doesn't reward the owners // this can happen if there are more than RemoveKeysLimit keys to remove - winner_info.outcome_info = outcome_info; - >::insert(market_id, winner_info); + gd_info.outcome_info = outcome_info; + >::insert(market_id, gd_info); } let mut removed_keys_amount = 0u32; @@ -337,21 +381,21 @@ mod pallet { >::OutcomesNotFullyCleaned ); - let winner_info = - >::get(market_id).ok_or(Error::::NoGlobalDisputeStarted)?; - ensure!(winner_info.is_finished, Error::::UnfinishedGlobalDispute); + let gd_info = >::get(market_id) + .ok_or(Error::::NoGlobalDisputeStarted)?; + ensure!(gd_info.status == GDStatus::Finished, Error::::UnfinishedGlobalDispute); let reward_account = Self::reward_account(&market_id); let reward_account_free_balance = T::Currency::free_balance(&reward_account); ensure!(!reward_account_free_balance.is_zero(), Error::::NoFundsToReward); - let owners_len = winner_info.outcome_info.owners.len() as u32; + let owners_len = gd_info.outcome_info.owners.len() as u32; let mut remainder = reward_account_free_balance; let owners_len_in_balance: BalanceOf = >::from(owners_len); if let Some(reward_per_each) = reward_account_free_balance.checked_div(&owners_len_in_balance) { - for winner in winner_info.outcome_info.owners.iter() { + for winner in gd_info.outcome_info.owners.iter() { // *Should* always be equal to `reward_per_each` let reward = remainder.min(reward_per_each); remainder = remainder.saturating_sub(reward); @@ -378,7 +422,7 @@ mod pallet { Self::deposit_event(Event::OutcomeOwnersRewarded { market_id, - owners: winner_info.outcome_info.owners.to_vec(), + owners: gd_info.outcome_info.owners.to_vec(), }); Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(owners_len))).into()) @@ -413,9 +457,9 @@ mod pallet { ensure!(amount <= voter_free_balance, Error::::InsufficientAmount); ensure!(amount >= T::MinOutcomeVoteAmount::get(), Error::::AmountTooLow); - let winner_info = - >::get(market_id).ok_or(Error::::NoGlobalDisputeStarted)?; - ensure!(!winner_info.is_finished, Error::::GlobalDisputeAlreadyFinished); + let gd_info = >::get(market_id) + .ok_or(Error::::NoGlobalDisputeStarted)?; + ensure!(gd_info.status == GDStatus::Active, Error::::InvalidGlobalDisputeStatus); let mut outcome_info = >::get(market_id, &outcome).ok_or(Error::::OutcomeDoesNotExist)?; @@ -472,7 +516,7 @@ mod pallet { Ok(Some(T::WeightInfo::vote_on_outcome(outcome_owners_len, vote_lock_counter)).into()) } - /// Return all locked native tokens in a global dispute. + /// Return all locked native tokens from a finished or destroyed global dispute. /// Fails if the global dispute is not concluded yet. /// /// # Arguments @@ -505,13 +549,13 @@ mod pallet { let mut lock_info = >::get(&voter); let vote_lock_counter = lock_info.len() as u32; // Inside retain we follow these rules: - // 1. Remove all locks from resolved (/ finished) global disputes. + // 1. Remove all locks from resolved (/ finished / destroyed) global disputes. // 2. Then find the maximum lock from all unresolved global disputes. lock_info.retain(|&(market_id, locked_balance)| { // weight component MaxOwners comes from querying the winner information - match >::get(market_id) { - Some(winner_info) => { - if winner_info.is_finished { + match >::get(market_id) { + Some(gd_info) => { + if matches!(gd_info.status, GDStatus::Finished | GDStatus::Destroyed) { false } else { lock_needed = lock_needed.max(locked_balance); @@ -564,14 +608,14 @@ mod pallet { } fn update_winner(market_id: &MarketIdOf, outcome: &OutcomeReport, amount: BalanceOf) { - >::mutate(market_id, |highest: &mut Option>| { + >::mutate(market_id, |highest: &mut Option>| { *highest = Some(highest.clone().map_or( - WinnerInfo::new(outcome.clone(), amount), - |prev_winner_info| { - if amount >= prev_winner_info.outcome_info.outcome_sum { - WinnerInfo::new(outcome.clone(), amount) + GDInfo::new(outcome.clone(), amount), + |prev_gd_info| { + if amount >= prev_gd_info.outcome_info.outcome_sum { + GDInfo::new(outcome.clone(), amount) } else { - prev_winner_info + prev_gd_info } }, )); @@ -589,8 +633,8 @@ mod pallet { owner: &T::AccountId, initial_vote_balance: BalanceOf, ) -> DispatchResult { - match >::get(market_id) { - Some(winner_info) if winner_info.is_finished => { + match >::get(market_id) { + Some(gd_info) if gd_info.status == GDStatus::Finished => { return Err(Error::::GlobalDisputeAlreadyFinished.into()); } _ => (), @@ -631,11 +675,11 @@ mod pallet { } fn determine_voting_winner(market_id: &MarketIdOf) -> Option { - match >::get(market_id) { - Some(mut winner_info) => { - winner_info.is_finished = true; - let winner_outcome = winner_info.outcome.clone(); - >::insert(market_id, winner_info); + match >::get(market_id) { + Some(mut gd_info) => { + gd_info.status = GDStatus::Finished; + let winner_outcome = gd_info.outcome.clone(); + >::insert(market_id, gd_info); Self::deposit_event(Event::GlobalDisputeWinnerDetermined { market_id: *market_id, }); @@ -646,7 +690,16 @@ mod pallet { } fn is_started(market_id: &MarketIdOf) -> bool { - >::get(market_id).is_some() + >::get(market_id).is_some() + } + + fn destroy_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { + >::try_mutate(market_id, |gd_info| { + let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::NoGlobalDisputeStarted)?; + raw_gd_info.status = GDStatus::Destroyed; + *gd_info = Some(raw_gd_info.clone()); + Ok(()) + }) } } } diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 3801890b5..ed2ec195c 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -11,20 +11,30 @@ pub struct OutcomeInfo { pub owners: OwnerInfo, } -/// The information about the current highest winning outcome. +/// The general information about the global dispute. #[derive(TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct WinnerInfo { +pub struct GDInfo { /// The outcome, which is in the lead. pub outcome: OutcomeReport, /// The information about the winning outcome. pub outcome_info: OutcomeInfo, - /// Check, if the global dispute is finished. - pub is_finished: bool, + /// The current status of the global dispute. + pub status: GDStatus, } -impl WinnerInfo { +impl GDInfo { pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, owners: Default::default() }; - WinnerInfo { outcome, is_finished: false, outcome_info } + GDInfo { outcome, status: GDStatus::Active, outcome_info } } } + +#[derive(TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] +pub enum GDStatus { + /// The global dispute is in progress. + Active, + /// The global dispute is finished. + Finished, + /// The global dispute was triggered to get destroyed. + Destroyed, +} From 6611b2540956a4487dddc913f849acf8fb64279b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 23 Dec 2022 14:57:23 +0100 Subject: [PATCH 007/368] add possession enum --- runtime/battery-station/src/parameters.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/global-disputes/README.md | 2 +- zrml/global-disputes/src/benchmarks.rs | 81 ++++--- .../src/global_disputes_pallet_api.rs | 2 +- zrml/global-disputes/src/lib.rs | 199 ++++++++++----- zrml/global-disputes/src/tests.rs | 228 +++++++----------- zrml/global-disputes/src/types.rs | 49 +++- zrml/prediction-markets/src/lib.rs | 4 +- 9 files changed, 318 insertions(+), 251 deletions(-) diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 68bf1c76d..8f212cbf6 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -352,7 +352,7 @@ parameter_types! { pub const GlobalDisputesPalletId: PalletId = GLOBAL_DISPUTES_PALLET_ID; /// The period for a global dispute to end. pub const GlobalDisputePeriod: BlockNumber = 3 * BLOCKS_PER_DAY; - /// The maximum number of owners for a voting outcome for private API calls of `push_voting_outcome`. + /// The maximum number of owners for a voting outcome for private API calls of `push_vote_outcome`. pub const MaxOwners: u32 = 10; /// The maximum number of market ids (participate in multiple different global disputes at the same time) for one account to vote on outcomes. pub const MaxGlobalDisputeVotes: u32 = 50; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 062f42f7e..98f378e32 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -352,7 +352,7 @@ parameter_types! { pub const GlobalDisputesPalletId: PalletId = GLOBAL_DISPUTES_PALLET_ID; /// The period for a global dispute to end. pub const GlobalDisputePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; - /// The maximum number of owners for a voting outcome for private API calls of `push_voting_outcome`. + /// The maximum number of owners for a voting outcome for private API calls of `push_vote_outcome`. pub const MaxOwners: u32 = 10; /// The maximum number of market ids (participate in multiple different global disputes at the same time) for one account to vote on outcomes. pub const MaxGlobalDisputeVotes: u32 = 50; diff --git a/zrml/global-disputes/README.md b/zrml/global-disputes/README.md index b1bbf73fd..2a93c9b7d 100644 --- a/zrml/global-disputes/README.md +++ b/zrml/global-disputes/README.md @@ -35,7 +35,7 @@ on which the market finally resolves. #### Private Pallet API -- `push_voting_outcome` - Start a global dispute, add an initial voting outcome +- `push_vote_outcome` - Start a global dispute, add an initial voting outcome and vote on it. - `determine_voting_winner` - Determine the canonical voting outcome based on total locked tokens. diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index b45f6d7e0..c5d4b42a4 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -54,7 +54,7 @@ fn assert_last_event(generic_event: ::Event) { benchmarks! { vote_on_outcome { - // only Outcomes owners, but not Winners owners is present during vote_on_outcome + // only Outcomes owners, but not GlobalDisputesInfo owners is present during vote_on_outcome let o in 1..T::MaxOwners::get(); // ensure we have one vote left for the call @@ -69,7 +69,7 @@ benchmarks! { deposit::(&caller); for i in 1..=o { let owner = account("outcomes_owner", i, 0); - GlobalDisputes::::push_voting_outcome( + GlobalDisputes::::push_vote_outcome( &market_id, outcome.clone(), &owner, @@ -92,9 +92,10 @@ benchmarks! { // minus one to ensure, that we use the worst case // for using a new winner info after the vote_on_outcome call let vote_sum = amount - 1u128.saturated_into(); - let outcome_info = OutcomeInfo { outcome_sum: vote_sum, owners: Default::default() }; - let winner_info = WinnerInfo {outcome: outcome.clone(), is_finished: false, outcome_info}; - >::insert(market_id, winner_info); + let possession = Some(Possession::Shared { owners: Default::default() }); + let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; + let gd_info = GDInfo {winner_outcome: outcome.clone(), status: GDStatus::Active, outcome_info}; + >::insert(market_id, gd_info); }: _(RawOrigin::Signed(caller.clone()), market_id, outcome.clone(), amount) verify { assert_last_event::( @@ -120,10 +121,11 @@ benchmarks! { } let owners = BoundedVec::try_from(owners).unwrap(); let outcome = OutcomeReport::Scalar(0); - let outcome_info = OutcomeInfo { outcome_sum: vote_sum, owners }; + let possession = Some(Possession::Shared { owners }); + let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; // is_finished is false, // because we need `lock_needed` to be greater zero to set a lock. - let winner_info = WinnerInfo {outcome, is_finished: false, outcome_info}; + let gd_info = GDInfo {winner_outcome: outcome, status: GDStatus::Active, outcome_info}; let caller: T::AccountId = whitelisted_caller(); let voter: T::AccountId = account("voter", 0, 0); @@ -134,7 +136,7 @@ benchmarks! { let market_id: MarketIdOf = i.saturated_into(); let locked_balance: BalanceOf = i.saturated_into(); vote_locks.try_push((market_id, locked_balance)).unwrap(); - >::insert(market_id, winner_info.clone()); + >::insert(market_id, gd_info.clone()); } >::insert(voter.clone(), vote_locks.clone()); }: { @@ -160,10 +162,11 @@ benchmarks! { } let owners = BoundedVec::try_from(owners).unwrap(); let outcome = OutcomeReport::Scalar(0); - let outcome_info = OutcomeInfo { outcome_sum: vote_sum, owners }; + let possession = Some(Possession::Shared { owners }); + let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; // is_finished is true, // because we need `lock_needed` to be zero to remove all locks. - let winner_info = WinnerInfo {outcome, is_finished: true, outcome_info}; + let gd_info = GDInfo {winner_outcome: outcome, status: GDStatus::Finished, outcome_info}; let caller: T::AccountId = whitelisted_caller(); let voter: T::AccountId = account("voter", 0, 0); @@ -176,7 +179,7 @@ benchmarks! { let market_id: MarketIdOf = i.saturated_into(); let locked_balance: BalanceOf = 1u128.saturated_into(); vote_locks.try_push((market_id, locked_balance)).unwrap(); - >::insert(market_id, winner_info.clone()); + >::insert(market_id, gd_info.clone()); } >::insert(voter.clone(), vote_locks); }: { @@ -193,8 +196,8 @@ benchmarks! { add_vote_outcome { // concious decision for using component 0..MaxOwners here // because although we check that is_finished is false, - // Winners counts processing time for the decoding of the owners vector. - // then if the owner information is not present on Winners, + // GlobalDisputesInfo counts processing time for the decoding of the owners vector. + // then if the owner information is not present on GlobalDisputesInfo, // the owner info is present on Outcomes // this happens in the case, that Outcomes is not none at the query time. let w in 1..T::MaxOwners::get(); @@ -205,10 +208,11 @@ benchmarks! { owners.push(owner); } let owners = BoundedVec::try_from(owners).unwrap(); - let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), owners }; - let winner_info = WinnerInfo { - outcome: OutcomeReport::Scalar(0), - is_finished: false, + let possession = Some(Possession::Shared { owners }); + let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession }; + let gd_info = GDInfo { + winner_outcome: OutcomeReport::Scalar(0), + status: GDStatus::Active, outcome_info, }; @@ -217,23 +221,26 @@ benchmarks! { let market = market_mock::(); T::MarketCommons::push_market(market).unwrap(); let outcome = OutcomeReport::Scalar(20); - >::insert(market_id, winner_info); + >::insert(market_id, gd_info); deposit::(&caller); }: _(RawOrigin::Signed(caller.clone()), market_id, outcome.clone()) verify { assert_last_event::(Event::AddedVotingOutcome:: { market_id, - owner: caller, + owner: caller.clone(), outcome: outcome.clone(), }.into()); - let winner_info = >::get(market_id).unwrap(); - assert_eq!(winner_info.outcome_info.outcome_sum, T::VotingOutcomeFee::get()); - // zero owners as long as dispute not finished and reward_outcome_owner not happened - assert_eq!(winner_info.outcome_info.owners.len(), 0usize); + let gd_info = >::get(market_id).unwrap(); + assert_eq!(gd_info.outcome_info.outcome_sum, T::VotingOutcomeFee::get()); + // None as long as dispute not finished and reward_outcome_owner not happened + assert_eq!(gd_info.outcome_info.possession, None); let outcomes_item = >::get(market_id, outcome).unwrap(); assert_eq!(outcomes_item.outcome_sum, T::VotingOutcomeFee::get()); - assert_eq!(outcomes_item.owners.len(), 1usize); + assert_eq!( + outcomes_item.possession.unwrap(), + Possession::Paid { owner: caller, fee: T::VotingOutcomeFee::get() }, + ); } reward_outcome_owner_with_funds { @@ -247,16 +254,16 @@ benchmarks! { owners_vec.push(owner); } let owners = BoundedVec::try_from(owners_vec.clone()).unwrap(); - - let winner_info = WinnerInfo { - outcome: OutcomeReport::Scalar(0), - is_finished: true, + let possession = Some(Possession::Shared { owners }); + let gd_info = GDInfo { + winner_outcome: OutcomeReport::Scalar(0), + status: GDStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 42u128.saturated_into(), - owners, + possession, }, }; - >::insert(market_id, winner_info.clone()); + >::insert(market_id, gd_info.clone()); let reward_account = GlobalDisputes::::reward_account(&market_id); let _ = T::Currency::deposit_creating( @@ -276,7 +283,7 @@ benchmarks! { ) .unwrap(); } verify { - assert!(winner_info.outcome_info.owners.len() == o as usize); + assert!(gd_info.outcome_info.possession.unwrap().get_shared_owners().unwrap().len() == o as usize); assert_last_event::( Event::OutcomeOwnersRewarded:: { market_id, @@ -297,7 +304,7 @@ benchmarks! { for i in 1..=k { let owner = account("outcomes_owner", i, 0); - GlobalDisputes::::push_voting_outcome( + GlobalDisputes::::push_vote_outcome( &market_id, OutcomeReport::Scalar(i.into()), &owner, @@ -314,18 +321,20 @@ benchmarks! { let owners = BoundedVec::try_from(owners.clone()).unwrap(); let winner_outcome = OutcomeReport::Scalar(0); + let possession = Some(Possession::Shared { owners }); let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), - owners + possession, }; >::insert(market_id, winner_outcome.clone(), outcome_info); + let possession = Some(Possession::Shared { owners: Default::default() }); let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), - owners: Default::default() + possession, }; - let winner_info = WinnerInfo {outcome: winner_outcome, is_finished: true, outcome_info}; - >::insert(market_id, winner_info); + let gd_info = GDInfo {winner_outcome, status: GDStatus::Finished, outcome_info}; + >::insert(market_id, gd_info); let caller: T::AccountId = whitelisted_caller(); diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 90eafcc9e..e98f85cbe 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -35,7 +35,7 @@ pub trait GlobalDisputesPalletApi { /// /// Returns the dispute mechanism's report if available, otherwise `None`. If `None` is /// returned, this means that the dispute could not be resolved. - fn push_voting_outcome( + fn push_vote_outcome( market_id: &MarketId, outcome: OutcomeReport, owner: &AccountId, diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index eaeaa2e8a..4a93b731d 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -84,7 +84,7 @@ mod pallet { type MaxGlobalDisputeVotes: Get; /// The maximum number of owners - /// for a voting outcome for private API calls of `push_voting_outcome`. + /// for a voting outcome for private API calls of `push_vote_outcome`. #[pallet::constant] type MaxOwners: Get; @@ -108,12 +108,13 @@ mod pallet { <::MarketCommons as MarketCommonsPalletApi>::MarketId; pub type AccountIdOf = ::AccountId; - pub type OutcomeInfoOf = OutcomeInfo, OwnerInfoOf>; - pub type GDInfoOf = GDInfo, OwnerInfoOf>; + pub type OutcomeInfoOf = OutcomeInfo, BalanceOf, OwnerInfoOf>; + pub type GDInfoOf = GDInfo, BalanceOf, OwnerInfoOf>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; type OwnerInfoOf = BoundedVec, ::MaxOwners>; pub type LockInfoOf = BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; + type RewardInfoOf = RewardInfo, AccountIdOf, BalanceOf>; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -159,8 +160,10 @@ mod pallet { }, /// The winner of the global dispute system is determined. GlobalDisputeWinnerDetermined { market_id: MarketIdOf }, - /// The outcome owner has been rewarded. + /// The outcome owners have been rewarded. OutcomeOwnersRewarded { market_id: MarketIdOf, owners: Vec> }, + /// The outcome owner has been rewarded. + OutcomeOwnerRewarded { market_id: MarketIdOf, owner: AccountIdOf }, /// The outcomes storage item is partially cleaned. OutcomesPartiallyCleaned { market_id: MarketIdOf }, /// The outcomes storage item is fully cleaned. @@ -186,22 +189,26 @@ mod pallet { InsufficientAmount, /// The maximum amount of owners is reached. MaxOwnersReached, + /// The maximum number of votes for this account is reached. + MaxVotesReached, /// The amount in the reward pot is zero. NoFundsToReward, /// No global dispute present at the moment. NoGlobalDisputeStarted, + /// There is no owner information for this outcome. + NoPossession, /// The voting outcome has been already added. OutcomeAlreadyExists, /// The outcome specified is not present in the voting outcomes. OutcomeDoesNotExist, /// Submitted outcome does not match market type. OutcomeMismatch, - /// The global dispute period is not over yet. The winner is not yet determined. - UnfinishedGlobalDispute, - /// The maximum number of votes for this account is reached. - MaxVotesReached, /// The outcomes are not fully cleaned yet. OutcomesNotFullyCleaned, + /// Only a shared possession is allowed. + SharedPossessionRequired, + /// The global dispute period is not over yet. The winner is not yet determined. + UnfinishedGlobalDispute, } #[pallet::call] @@ -242,7 +249,7 @@ mod pallet { let voting_outcome_fee = T::VotingOutcomeFee::get(); - Self::push_voting_outcome(&market_id, outcome.clone(), &owner, voting_outcome_fee)?; + // Self::push_vote_outcome(&market_id, outcome.clone(), &owner, voting_outcome_fee)?; let reward_account = Self::reward_account(&market_id); @@ -253,6 +260,12 @@ mod pallet { ExistenceRequirement::AllowDeath, )?; + Self::update_winner(&market_id, &outcome, voting_outcome_fee); + let possession = + Some(Possession::Paid { owner: owner.clone(), fee: voting_outcome_fee }); + let outcome_info = OutcomeInfo { outcome_sum: voting_outcome_fee, possession }; + >::insert(market_id, outcome.clone(), outcome_info); + Self::deposit_event(Event::AddedVotingOutcome { market_id, owner, outcome }); // charge weight for successfully have no owners in Winners // this is the case, because owners are not inserted @@ -287,7 +300,20 @@ mod pallet { for (_, outcome_info) in >::drain_prefix(market_id).take(T::RemoveKeysLimit::get() as usize) { - owners_len = owners_len.max(outcome_info.owners.len() as u32); + match outcome_info.possession { + Some(Possession::Paid { owner, fee }) => { + T::Currency::transfer( + &Self::reward_account(&market_id), + &owner, + fee, + ExistenceRequirement::AllowDeath, + )?; + } + Some(Possession::Shared { owners }) => { + owners_len = owners_len.max(owners.len() as u32); + } + None => (), + } removed_keys_amount = removed_keys_amount.saturating_add(1u32); } @@ -328,11 +354,13 @@ mod pallet { ensure!(gd_info.status == GDStatus::Finished, Error::::UnfinishedGlobalDispute); let winning_outcome: Option> = - >::get(market_id, &gd_info.outcome); + >::get(market_id, &gd_info.winner_outcome); let mut owners_len = 0u32; - // move the winning outcome info to Winners before it gets drained + // move the winning outcome info to GlobalDisputesInfo before it gets drained if let Some(outcome_info) = winning_outcome { - owners_len = outcome_info.owners.len() as u32; + if let Some(Possession::Shared { owners }) = &outcome_info.possession { + owners_len = owners.len() as u32; + } // storage write is needed here in case, // that the first call of reward_outcome_owner doesn't reward the owners // this can happen if there are more than RemoveKeysLimit keys to remove @@ -341,10 +369,12 @@ mod pallet { } let mut removed_keys_amount = 0u32; - for (_, i) in + for (_, outcome_info) in >::drain_prefix(market_id).take(T::RemoveKeysLimit::get() as usize) { - owners_len = owners_len.max(i.owners.len() as u32); + if let Some(Possession::Shared { owners }) = outcome_info.possession { + owners_len = owners_len.max(owners.len() as u32); + } removed_keys_amount = removed_keys_amount.saturating_add(1u32); } @@ -388,44 +418,22 @@ mod pallet { let reward_account = Self::reward_account(&market_id); let reward_account_free_balance = T::Currency::free_balance(&reward_account); ensure!(!reward_account_free_balance.is_zero(), Error::::NoFundsToReward); - let owners_len = gd_info.outcome_info.owners.len() as u32; - let mut remainder = reward_account_free_balance; - let owners_len_in_balance: BalanceOf = >::from(owners_len); - if let Some(reward_per_each) = - reward_account_free_balance.checked_div(&owners_len_in_balance) - { - for winner in gd_info.outcome_info.owners.iter() { - // *Should* always be equal to `reward_per_each` - let reward = remainder.min(reward_per_each); - remainder = remainder.saturating_sub(reward); - // Reward the loosing funds to the winners - let res = T::Currency::transfer( - &reward_account, - winner, - reward, - ExistenceRequirement::AllowDeath, - ); - // not really much we can do if it fails - debug_assert!( - res.is_ok(), - "Global Disputes: Rewarding a outcome owner failed." - ); - } - } else { - log::error!( - "Global Disputes: There should be always at least one owner for a voting \ - outcome." - ); - debug_assert!(false); - } - - Self::deposit_event(Event::OutcomeOwnersRewarded { + let reward_info = RewardInfo { market_id, - owners: gd_info.outcome_info.owners.to_vec(), - }); + reward: reward_account_free_balance, + source: reward_account, + }; - Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(owners_len))).into()) + match gd_info.outcome_info.possession { + Some(Possession::Shared { owners }) => { + Self::reward_shared_possession(reward_info, owners) + } + Some(Possession::Paid { owner, fee: _ }) => { + Self::reward_paid_possession(reward_info, owner) + } + None => Err(Error::::NoPossession.into()), + } } /// Vote on existing voting outcomes by locking native tokens. @@ -463,7 +471,11 @@ mod pallet { let mut outcome_info = >::get(market_id, &outcome).ok_or(Error::::OutcomeDoesNotExist)?; - let outcome_owners_len = outcome_info.owners.len() as u32; + let outcome_owners_len = match outcome_info.possession { + Some(Possession::Shared { ref owners }) => owners.len() as u32, + Some(Possession::Paid { .. }) => 1u32, + None => 0u32, + }; // The `outcome_sum` never decreases (only increases) to allow // caching the outcome with the highest `outcome_sum`. @@ -621,13 +633,70 @@ mod pallet { )); }); } + + fn reward_shared_possession( + reward_info: RewardInfoOf, + owners: OwnerInfoOf, + ) -> DispatchResultWithPostInfo { + let mut remainder = reward_info.reward; + let owners_len = owners.len() as u32; + let owners_len_in_balance: BalanceOf = >::from(owners_len); + if let Some(reward_per_each) = reward_info.reward.checked_div(&owners_len_in_balance) { + for winner in owners.iter() { + // *Should* always be equal to `reward_per_each` + let reward = remainder.min(reward_per_each); + remainder = remainder.saturating_sub(reward); + // Reward the loosing funds to the winners + let res = T::Currency::transfer( + &reward_info.source, + winner, + reward, + ExistenceRequirement::AllowDeath, + ); + // not really much we can do if it fails + debug_assert!( + res.is_ok(), + "Global Disputes: Rewarding a outcome owner failed." + ); + } + } else { + log::error!( + "Global Disputes: There should be always at least one owner for a voting \ + outcome." + ); + debug_assert!(false); + } + Self::deposit_event(Event::OutcomeOwnersRewarded { + market_id: reward_info.market_id, + owners: owners.into_inner(), + }); + Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(owners_len))).into()) + } + + fn reward_paid_possession( + reward_info: RewardInfoOf, + owner: AccountIdOf, + ) -> DispatchResultWithPostInfo { + let res = T::Currency::transfer( + &reward_info.source, + &owner, + reward_info.reward, + ExistenceRequirement::AllowDeath, + ); + debug_assert!(res.is_ok(), "Global Disputes: Rewarding outcome owner failed."); + Self::deposit_event(Event::OutcomeOwnerRewarded { + market_id: reward_info.market_id, + owner, + }); + Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(1u32))).into()) + } } impl GlobalDisputesPalletApi, AccountIdOf, BalanceOf> for Pallet where T: Config, { - fn push_voting_outcome( + fn push_vote_outcome( market_id: &MarketIdOf, outcome: OutcomeReport, owner: &T::AccountId, @@ -643,10 +712,12 @@ mod pallet { Some(mut outcome_info) => { let outcome_sum = outcome_info.outcome_sum.saturating_add(initial_vote_balance); outcome_info.outcome_sum = outcome_sum; - outcome_info - .owners - .try_push(owner.clone()) - .map_err(|_| Error::::MaxOwnersReached)?; + let possession = outcome_info.possession.ok_or(Error::::NoPossession)?; + let mut owners = possession + .get_shared_owners() + .ok_or(Error::::SharedPossessionRequired)?; + owners.try_push(owner.clone()).map_err(|_| Error::::MaxOwnersReached)?; + outcome_info.possession = Some(Possession::Shared { owners }); Self::update_winner(market_id, &outcome, outcome_sum); >::insert(market_id, outcome, outcome_info); } @@ -654,8 +725,9 @@ mod pallet { // adding one item to BoundedVec can not fail if let Ok(owners) = BoundedVec::try_from(vec![owner.clone()]) { Self::update_winner(market_id, &outcome, initial_vote_balance); + let possession = Some(Possession::Shared { owners }); let outcome_info = - OutcomeInfo { outcome_sum: initial_vote_balance, owners }; + OutcomeInfo { outcome_sum: initial_vote_balance, possession }; >::insert(market_id, outcome, outcome_info); } else { log::error!("Global Disputes: Could not construct a bounded vector."); @@ -670,15 +742,22 @@ mod pallet { market_id: &MarketIdOf, outcome: &OutcomeReport, ) -> Option<(BalanceOf, Vec>)> { - >::get(market_id, outcome) - .map(|outcome_info| (outcome_info.outcome_sum, outcome_info.owners.into_inner())) + >::get(market_id, outcome).map(|outcome_info| { + match outcome_info.possession { + Some(Possession::Shared { owners }) => { + (outcome_info.outcome_sum, owners.into_inner()) + } + Some(Possession::Paid { owner, .. }) => (outcome_info.outcome_sum, vec![owner]), + None => (outcome_info.outcome_sum, vec![]), + } + }) } fn determine_voting_winner(market_id: &MarketIdOf) -> Option { match >::get(market_id) { Some(mut gd_info) => { gd_info.status = GDStatus::Finished; - let winner_outcome = gd_info.outcome.clone(); + let winner_outcome = gd_info.winner_outcome.clone(); >::insert(market_id, gd_info); Self::deposit_event(Event::GlobalDisputeWinnerDetermined { market_id: *market_id, diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 3e0e8e7a3..c4713136f 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -21,8 +21,8 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, market_mock, mock::*, - types::{OutcomeInfo, WinnerInfo}, - Error, Event, Locks, MarketIdOf, Outcomes, Winners, + types::{GDInfo, GDStatus, OutcomeInfo, Possession}, + Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, }; use frame_support::{ assert_noop, assert_ok, @@ -44,15 +44,15 @@ fn the_lock(amount: u128) -> BalanceLock { } fn setup_vote_outcomes_with_hundred(market_id: &MarketIdOf) { - GlobalDisputes::push_voting_outcome(market_id, OutcomeReport::Scalar(0), &ALICE, SETUP_AMOUNT) + GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(0), &ALICE, SETUP_AMOUNT) .unwrap(); - GlobalDisputes::push_voting_outcome(market_id, OutcomeReport::Scalar(20), &ALICE, SETUP_AMOUNT) + GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(20), &ALICE, SETUP_AMOUNT) .unwrap(); - GlobalDisputes::push_voting_outcome(market_id, OutcomeReport::Scalar(40), &ALICE, SETUP_AMOUNT) + GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(40), &ALICE, SETUP_AMOUNT) .unwrap(); - GlobalDisputes::push_voting_outcome(market_id, OutcomeReport::Scalar(60), &ALICE, SETUP_AMOUNT) + GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(60), &ALICE, SETUP_AMOUNT) .unwrap(); } @@ -65,7 +65,9 @@ fn check_outcome_sum( >::get(market_id, outcome).unwrap(), OutcomeInfo { outcome_sum: SETUP_AMOUNT + post_setup_amount, - owners: BoundedVec::try_from(vec![ALICE]).unwrap() + possession: Some(Possession::Shared { + owners: BoundedVec::try_from(vec![ALICE]).unwrap() + }) } ); } @@ -76,13 +78,8 @@ fn add_vote_outcome_works() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); let free_balance_alice_before = Balances::free_balance(&ALICE); let free_balance_reward_account = Balances::free_balance(GlobalDisputes::reward_account(&market_id)); @@ -117,13 +114,8 @@ fn add_vote_outcome_fails_with_outcome_mismatch() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); assert_noop!( GlobalDisputes::add_vote_outcome( Origin::signed(ALICE), @@ -139,13 +131,8 @@ fn add_vote_outcome_fails_with_outcome_mismatch() { fn add_vote_outcome_fails_with_non_existing_market() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); assert_noop!( GlobalDisputes::add_vote_outcome( Origin::signed(ALICE), @@ -180,9 +167,9 @@ fn add_vote_outcome_fails_if_global_dispute_finished() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - let mut winner_info = WinnerInfo::new(OutcomeReport::Scalar(0), 10 * BASE); - winner_info.is_finished = true; - >::insert(market_id, winner_info); + let mut gd_info = GDInfo::new(OutcomeReport::Scalar(0), 10 * BASE); + gd_info.status = GDStatus::Finished; + >::insert(market_id, gd_info); assert_noop!( GlobalDisputes::add_vote_outcome( @@ -190,7 +177,7 @@ fn add_vote_outcome_fails_if_global_dispute_finished() { market_id, OutcomeReport::Scalar(20), ), - Error::::GlobalDisputeAlreadyFinished + Error::::InvalidGlobalDisputeStatus ); }); } @@ -201,17 +188,15 @@ fn add_vote_outcome_fails_if_outcome_already_exists() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); >::insert( market_id, OutcomeReport::Scalar(20), - OutcomeInfo { outcome_sum: Zero::zero(), owners: Default::default() }, + OutcomeInfo { + outcome_sum: Zero::zero(), + possession: Some(Possession::Shared { owners: Default::default() }), + }, ); assert_noop!( GlobalDisputes::add_vote_outcome( @@ -230,13 +215,8 @@ fn add_vote_outcome_fails_if_balance_too_low() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); assert_noop!( GlobalDisputes::add_vote_outcome( Origin::signed(POOR_PAUL), @@ -257,19 +237,24 @@ fn reward_outcome_owner_works_for_multiple_owners() { OutcomeReport::Scalar(20), OutcomeInfo { outcome_sum: Zero::zero(), - owners: BoundedVec::try_from(vec![ALICE, BOB, CHARLIE]).unwrap(), + possession: Some(Possession::Shared { + owners: BoundedVec::try_from(vec![ALICE, BOB, CHARLIE]).unwrap(), + }), }, ); let _ = Balances::deposit_creating( &GlobalDisputes::reward_account(&market_id), 3 * VotingOutcomeFee::get(), ); - let winner_info = WinnerInfo { - outcome: OutcomeReport::Scalar(20), - is_finished: true, - outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, owners: Default::default() }, + let gd_info = GDInfo { + winner_outcome: OutcomeReport::Scalar(20), + status: GDStatus::Finished, + outcome_info: OutcomeInfo { + outcome_sum: 10 * BASE, + possession: Some(Possession::Shared { owners: Default::default() }), + }, }; - >::insert(market_id, winner_info); + >::insert(market_id, gd_info); let free_balance_alice_before = Balances::free_balance(&ALICE); let free_balance_bob_before = Balances::free_balance(&BOB); @@ -311,17 +296,22 @@ fn reward_outcome_owner_has_dust() { OutcomeReport::Scalar(20), OutcomeInfo { outcome_sum: Zero::zero(), - owners: BoundedVec::try_from(vec![ALICE, BOB, CHARLIE, EVE, POOR_PAUL, DAVE]) - .unwrap(), + possession: Some(Possession::Shared { + owners: BoundedVec::try_from(vec![ALICE, BOB, CHARLIE, EVE, POOR_PAUL, DAVE]) + .unwrap(), + }), }, ); let _ = Balances::deposit_creating(&GlobalDisputes::reward_account(&market_id), 100 * BASE); - let winner_info = WinnerInfo { - outcome: OutcomeReport::Scalar(20), - is_finished: true, - outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, owners: Default::default() }, + let gd_info = GDInfo { + winner_outcome: OutcomeReport::Scalar(20), + status: GDStatus::Finished, + outcome_info: OutcomeInfo { + outcome_sum: 10 * BASE, + possession: Some(Possession::Shared { owners: Default::default() }), + }, }; - >::insert(market_id, winner_info); + >::insert(market_id, gd_info); assert_ok!(GlobalDisputes::purge_outcomes(Origin::signed(ALICE), market_id,)); @@ -342,19 +332,26 @@ fn reward_outcome_owner_works_for_one_owner() { OutcomeReport::Scalar(20), OutcomeInfo { outcome_sum: Zero::zero(), - owners: BoundedVec::try_from(vec![ALICE]).unwrap(), + possession: Some(Possession::Shared { + owners: BoundedVec::try_from(vec![ALICE]).unwrap(), + }), }, ); let _ = Balances::deposit_creating( &GlobalDisputes::reward_account(&market_id), 3 * VotingOutcomeFee::get(), ); - let winner_info = WinnerInfo { - outcome: OutcomeReport::Scalar(20), - is_finished: true, - outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, owners: Default::default() }, + let gd_info = GDInfo { + winner_outcome: OutcomeReport::Scalar(20), + status: GDStatus::Finished, + outcome_info: OutcomeInfo { + outcome_sum: 10 * BASE, + possession: Some(Possession::Shared { + owners: BoundedVec::try_from(vec![]).unwrap(), + }), + }, }; - >::insert(market_id, winner_info); + >::insert(market_id, gd_info); assert_ok!(GlobalDisputes::purge_outcomes(Origin::signed(ALICE), market_id,)); @@ -381,34 +378,14 @@ fn reward_outcome_owner_works_for_one_owner() { fn vote_fails_if_amount_below_min_outcome_vote_amount() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(20), - &ALICE, - 20 * BASE, - ) - .unwrap(); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(40), - &ALICE, - 30 * BASE, - ) - .unwrap(); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(60), - &ALICE, - 40 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(20), &ALICE, 20 * BASE) + .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(40), &ALICE, 30 * BASE) + .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(60), &ALICE, 40 * BASE) + .unwrap(); assert_noop!( GlobalDisputes::vote_on_outcome( @@ -426,20 +403,10 @@ fn vote_fails_if_amount_below_min_outcome_vote_amount() { fn vote_fails_for_insufficient_funds() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(20), - &ALICE, - 20 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(20), &ALICE, 20 * BASE) + .unwrap(); // Paul does not have 50 * BASE assert_noop!( GlobalDisputes::vote_on_outcome( @@ -540,7 +507,7 @@ fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { free_balance_disputor_before - reserved_balance_disputor ); - GlobalDisputes::push_voting_outcome( + GlobalDisputes::push_vote_outcome( &market_id, OutcomeReport::Scalar(0), &ALICE, @@ -548,7 +515,7 @@ fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { ) .unwrap(); - GlobalDisputes::push_voting_outcome( + GlobalDisputes::push_vote_outcome( &market_id, OutcomeReport::Scalar(20), &ALICE, @@ -689,7 +656,10 @@ fn determine_voting_winner_works_four_outcome_votes() { OutcomeReport::Scalar(40) ); - assert!(>::get(market_id).unwrap().is_finished); + assert_eq!( + >::get(market_id).unwrap().status, + GDStatus::Finished + ); }); } @@ -905,34 +875,14 @@ fn unlock_clears_lock_info() { fn vote_fails_if_outcome_does_not_exist() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - 10 * BASE, - ) - .unwrap(); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(20), - &ALICE, - 20 * BASE, - ) - .unwrap(); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(40), - &ALICE, - 30 * BASE, - ) - .unwrap(); - GlobalDisputes::push_voting_outcome( - &market_id, - OutcomeReport::Scalar(60), - &ALICE, - 40 * BASE, - ) - .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) + .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(20), &ALICE, 20 * BASE) + .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(40), &ALICE, 30 * BASE) + .unwrap(); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(60), &ALICE, 40 * BASE) + .unwrap(); assert_noop!( GlobalDisputes::vote_on_outcome( diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index ed2ec195c..fdc11b1f5 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -2,34 +2,57 @@ use frame_support::pallet_prelude::{Decode, Encode, MaxEncodedLen, TypeInfo}; use sp_runtime::traits::Saturating; use zeitgeist_primitives::types::OutcomeReport; +#[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] +pub enum Possession { + Paid { owner: AccountId, fee: Balance }, + Shared { owners: Owners }, +} + +impl Possession { + pub fn is_shared(&self) -> bool { + matches!(self, Possession::Shared { .. }) + } + + pub fn get_shared_owners(self) -> Option { + match self { + Possession::Shared { owners } => Some(owners), + _ => None, + } + } + + pub fn is_paid(&self) -> bool { + matches!(self, Possession::Paid { .. }) + } +} + /// The information about a voting outcome of a global dispute. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct OutcomeInfo { +pub struct OutcomeInfo { /// The current sum of all locks on this outcome. pub outcome_sum: Balance, - /// The vector of owners of the outcome. - pub owners: OwnerInfo, + /// The information about the owner(s) and optionally additional fee. + pub possession: Option>, } /// The general information about the global dispute. #[derive(TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct GDInfo { +pub struct GDInfo { /// The outcome, which is in the lead. - pub outcome: OutcomeReport, + pub winner_outcome: OutcomeReport, /// The information about the winning outcome. - pub outcome_info: OutcomeInfo, + pub outcome_info: OutcomeInfo, /// The current status of the global dispute. pub status: GDStatus, } -impl GDInfo { +impl GDInfo { pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { - let outcome_info = OutcomeInfo { outcome_sum: vote_sum, owners: Default::default() }; - GDInfo { outcome, status: GDStatus::Active, outcome_info } + let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession: None }; + GDInfo { winner_outcome: outcome, status: GDStatus::Active, outcome_info } } } -#[derive(TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] +#[derive(TypeInfo, Debug, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub enum GDStatus { /// The global dispute is in progress. Active, @@ -38,3 +61,9 @@ pub enum GDStatus { /// The global dispute was triggered to get destroyed. Destroyed, } + +pub struct RewardInfo { + pub market_id: MarketId, + pub reward: Balance, + pub source: AccountId, +} diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 0d327a4d7..0898d05d8 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1263,7 +1263,7 @@ mod pallet { // add report outcome to voting choices if let Some(report) = market.report { - T::GlobalDisputes::push_voting_outcome( + T::GlobalDisputes::push_vote_outcome( &market_id, report.outcome, &report.by, @@ -1273,7 +1273,7 @@ mod pallet { for (index, MarketDispute { at: _, by, outcome }) in disputes.iter().enumerate() { let dispute_bond = default_dispute_bond::(index); - T::GlobalDisputes::push_voting_outcome( + T::GlobalDisputes::push_vote_outcome( &market_id, outcome.clone(), by, From aae2b019bfbbe431bddad4fe62c39f6b1710b222 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 3 Jan 2023 13:28:39 +0100 Subject: [PATCH 008/368] add migration for global disputes --- Makefile | 15 + runtime/common/src/lib.rs | 24 +- zrml/global-disputes/src/lib.rs | 18 +- zrml/global-disputes/src/migrations.rs | 391 +++++++++++++++++++++++++ zrml/global-disputes/src/types.rs | 31 +- 5 files changed, 473 insertions(+), 6 deletions(-) create mode 100644 zrml/global-disputes/src/migrations.rs diff --git a/Makefile b/Makefile index 129668d69..49f133974 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,21 @@ try-runtime-upgrade-battery-station: try-runtime-upgrade-zeitgeist: @$(MAKE) TRYRUNTIME_CHAIN="zeitgeist_staging" TRYRUNTIME_URL="wss://zeitgeist-rpc.dwellir.com:443" -- --try-runtime +--try-runtime-with-global-disputes: + RUST_LOG=runtime=trace,try-runtime::cli=trace,executor=trace \ + cargo run \ + --bin=zeitgeist \ + --features=parachain,try-runtime,with-global-disputes \ + try-runtime \ + --execution=Native \ + --chain=${TRYRUNTIME_CHAIN} \ + on-runtime-upgrade \ + live \ + --uri=${TRYRUNTIME_URL} + +try-runtime-upgrade-battery-station-with-global-disputes: + @$(MAKE) TRYRUNTIME_CHAIN="battery_station_staging" TRYRUNTIME_URL="wss://bsr.zeitgeist.pm:443" -- --try-runtime-with-global-disputes + build: SKIP_WASM_BUILD= cargo build diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 6614c6128..ef3d5383b 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -51,7 +51,27 @@ macro_rules! decl_common_types { type Address = sp_runtime::MultiAddress; - #[cfg(feature = "parachain")] + #[cfg(all(feature = "parachain", feature = "with-global-disputes"))] + pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, + >; + + #[cfg(all(not(feature = "parachain"), feature = "with-global-disputes"))] + pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, + >; + + #[cfg(all(not(feature = "parachain"), not(feature = "with-global-disputes")))] pub type Executive = frame_executive::Executive< Runtime, Block, @@ -61,7 +81,7 @@ macro_rules! decl_common_types { (), >; - #[cfg(not(feature = "parachain"))] + #[cfg(all(not(feature = "parachain"), not(feature = "with-global-disputes")))] pub type Executive = frame_executive::Executive< Runtime, Block, diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 4a93b731d..d04671e6a 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -20,6 +20,7 @@ mod benchmarks; mod global_disputes_pallet_api; +pub mod migrations; mod mock; mod tests; pub mod types; @@ -40,7 +41,7 @@ mod pallet { sp_runtime::traits::StaticLookup, traits::{ Currency, ExistenceRequirement, Get, IsType, LockIdentifier, LockableCurrency, - WithdrawReasons, + StorageVersion, WithdrawReasons, }, Blake2_128Concat, BoundedVec, PalletId, Twox64Concat, }; @@ -55,6 +56,9 @@ mod pallet { pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::config] pub trait Config: frame_system::Config { /// The currency implementation used to lock tokens for voting. @@ -111,12 +115,16 @@ mod pallet { pub type OutcomeInfoOf = OutcomeInfo, BalanceOf, OwnerInfoOf>; pub type GDInfoOf = GDInfo, BalanceOf, OwnerInfoOf>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - type OwnerInfoOf = BoundedVec, ::MaxOwners>; + pub(crate) type OwnerInfoOf = BoundedVec, ::MaxOwners>; pub type LockInfoOf = BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; type RewardInfoOf = RewardInfo, AccountIdOf, BalanceOf>; + // TODO: to remove after the storage migration + pub type WinnerInfoOf = OldWinnerInfo, OwnerInfoOf>; + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); /// All highest lock information (vote id, outcome index and locked balance) @@ -141,11 +149,15 @@ mod pallet { /// Maps the market id to all information /// about the global dispute. - // TODO what storage migration after changing the name? #[pallet::storage] pub type GlobalDisputesInfo = StorageMap<_, Twox64Concat, MarketIdOf, GDInfoOf, OptionQuery>; + // TODO: to remove after the storage migration + #[pallet::storage] + pub type Winners = + StorageMap<_, Twox64Concat, MarketIdOf, WinnerInfoOf, OptionQuery>; + #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event diff --git a/zrml/global-disputes/src/migrations.rs b/zrml/global-disputes/src/migrations.rs new file mode 100644 index 000000000..4b3636eaa --- /dev/null +++ b/zrml/global-disputes/src/migrations.rs @@ -0,0 +1,391 @@ +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +// We use these utilities to prevent having to make the swaps pallet a dependency of +// prediciton-markets. The calls are based on the implementation of `StorageVersion`, found here: +// https://github.com/paritytech/substrate/blob/bc7a1e6c19aec92bfa247d8ca68ec63e07061032/frame/support/src/traits/metadata.rs#L168-L230 +// and previous migrations. + +extern crate alloc; + +use crate::{types::*, Config, Pallet as GDPallet, *}; +use frame_support::{ + dispatch::Weight, + log, + pallet_prelude::PhantomData, + traits::{Get, OnRuntimeUpgrade, StorageVersion}, +}; +use sp_runtime::traits::Saturating; + +#[cfg(feature = "try-runtime")] +use alloc::string::ToString; +#[cfg(feature = "try-runtime")] +use scale_info::prelude::format; + +const GD_REQUIRED_STORAGE_VERSION: u16 = 0; +const GD_NEXT_STORAGE_VERSION: u16 = 1; + +pub struct ModifyGlobalDisputesStructures(PhantomData); + +impl OnRuntimeUpgrade + for ModifyGlobalDisputesStructures +{ + fn on_runtime_upgrade() -> Weight + where + T: Config, + { + let mut total_weight = T::DbWeight::get().reads(1); + let gd_version = StorageVersion::get::>(); + if gd_version != GD_REQUIRED_STORAGE_VERSION { + log::info!( + "ModifyGlobalDisputesStructures: global disputes version is {:?}, require {:?};", + gd_version, + GD_REQUIRED_STORAGE_VERSION, + ); + return total_weight; + } + log::info!("ModifyGlobalDisputesStructures: Starting..."); + + for (market_id, winner_info) in crate::Winners::::iter() { + total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); + + let owners = winner_info.outcome_info.owners; + let owners_len = owners.len(); + let possession = match owners_len { + 0usize => None, + 1usize => Some(Possession::Paid { + owner: owners + .get(0) + .expect("Owners len is 1, but could not get this owner.") + .clone(), + fee: T::VotingOutcomeFee::get(), + }), + _ => Some(Possession::Shared { owners }), + }; + + let outcome_info = + OutcomeInfo { outcome_sum: winner_info.outcome_info.outcome_sum, possession }; + let gd_info = GDInfo { + winner_outcome: winner_info.outcome, + outcome_info, + status: GDStatus::Finished, + }; + crate::GlobalDisputesInfo::::insert(market_id, gd_info); + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // Look here https://github.com/paritytech/substrate/blob/7ba1ef526fbcc224d3bd446c83ee66a83cacec49/frame/assets/src/migration.rs#L69-L79 + let mut translated = 0u64; + Outcomes::::translate::, OwnerInfoOf>, _>( + |_key1, _key2, old_value| { + translated.saturating_inc(); + + let owners = old_value.owners; + let owners_len = owners.len(); + let possession = match owners_len { + 0usize => None, + 1usize => Some(Possession::Paid { + owner: owners + .get(0) + .expect("Owners len is 1, but could not get this owner.") + .clone(), + fee: T::VotingOutcomeFee::get(), + }), + _ => Some(Possession::Shared { owners }), + }; + + let new_value = OutcomeInfo { outcome_sum: old_value.outcome_sum, possession }; + + Some(new_value) + }, + ); + log::info!("ModifyGlobalDisputesStructures: Upgraded {} outcomes.", translated); + total_weight = total_weight + .saturating_add(T::DbWeight::get().reads_writes(translated + 1, translated + 1)); + + StorageVersion::new(GD_NEXT_STORAGE_VERSION).put::>(); + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); + log::info!("ModifyGlobalDisputesStructures: Done!"); + total_weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<(), &'static str> { + use frame_support::traits::OnRuntimeUpgradeHelpersExt; + + let mut counter = 0_u32; + for (market_id, winner_info) in crate::Winners::::iter() { + Self::set_temp_storage(winner_info, &format!("{:?}", market_id)); + + counter = counter.saturating_add(1_u32); + } + let counter_key = "counter_key".to_string(); + Self::set_temp_storage(counter, &counter_key); + Ok(()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade() -> Result<(), &'static str> { + use frame_support::traits::OnRuntimeUpgradeHelpersExt; + + let mut markets_count = 0_u32; + let old_counter_key = "counter_key".to_string(); + for (market_id, gd_info) in crate::GlobalDisputesInfo::::iter() { + let GDInfo { winner_outcome, outcome_info, status } = gd_info; + + let market_id_str = format!("{:?}", market_id); + + let winner_info: OldWinnerInfo, OwnerInfoOf> = + Self::get_temp_storage(&market_id_str) + .unwrap_or_else(|| panic!("old value not found for market id {:?}", market_id)); + + assert_eq!(winner_outcome, winner_info.outcome); + assert_eq!(status, GDStatus::Finished); + + let owners = winner_info.outcome_info.owners; + let owners_len = owners.len(); + + let possession = match owners_len { + 0usize => None, + 1usize => Some(Possession::Paid { + owner: owners + .get(0) + .expect("Owners len is 1, but could not get this owner.") + .clone(), + fee: T::VotingOutcomeFee::get(), + }), + _ => Some(Possession::Shared { owners }), + }; + + let outcome_info_expected = + OutcomeInfo { outcome_sum: winner_info.outcome_info.outcome_sum, possession }; + assert_eq!(outcome_info, outcome_info_expected); + + markets_count += 1_u32; + } + let old_markets_count: u32 = + Self::get_temp_storage(&old_counter_key).expect("old counter key storage not found"); + assert_eq!(markets_count, old_markets_count); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ExtBuilder, Runtime, ALICE, BOB}; + use frame_support::{ + migration::{get_storage_value, put_storage_value}, + BoundedVec, + }; + use sp_runtime::traits::SaturatedConversion; + use zeitgeist_primitives::{ + constants::mock::VotingOutcomeFee, + types::{MarketId, OutcomeReport}, + }; + + const GLOBAL_DISPUTES: &[u8] = b"GlobalDisputes"; + const GD_OUTCOMES: &[u8] = b"Outcomes"; + + type OldOutcomeInfoOf = OldOutcomeInfo, OwnerInfoOf>; + + #[test] + fn on_runtime_upgrade_increments_the_storage_versions() { + ExtBuilder::default().build().execute_with(|| { + set_up_chain(); + ModifyGlobalDisputesStructures::::on_runtime_upgrade(); + let gd_version = StorageVersion::get::>(); + assert_eq!(gd_version, GD_NEXT_STORAGE_VERSION); + }); + } + + #[test] + fn on_runtime_sets_new_global_disputes_storage_paid() { + ExtBuilder::default().build().execute_with(|| { + set_up_chain(); + + let market_id = 0u128; + + let outcome_sum = 42u128.saturated_into::>(); + let owners = BoundedVec::try_from(vec![ALICE]).unwrap(); + + let outcome_info = OldOutcomeInfo { outcome_sum, owners }; + let outcome = OutcomeReport::Categorical(42u16); + let winner_info = + OldWinnerInfo { outcome: outcome.clone(), outcome_info, is_finished: true }; + + crate::Winners::::insert(market_id, winner_info); + + ModifyGlobalDisputesStructures::::on_runtime_upgrade(); + + let possession = Some(Possession::Paid { owner: ALICE, fee: VotingOutcomeFee::get() }); + + let new_outcome_info = OutcomeInfo { outcome_sum, possession }; + + let expected = GDInfo { + winner_outcome: outcome, + outcome_info: new_outcome_info, + status: GDStatus::Finished, + }; + + let actual = crate::GlobalDisputesInfo::::get(market_id).unwrap(); + assert_eq!(expected, actual); + }); + } + + #[test] + fn on_runtime_sets_new_global_disputes_storage_shared() { + ExtBuilder::default().build().execute_with(|| { + set_up_chain(); + + let market_id = 0u128; + + let outcome_sum = 42u128.saturated_into::>(); + let owners = BoundedVec::try_from(vec![ALICE, BOB]).unwrap(); + + let outcome_info = OldOutcomeInfo { outcome_sum, owners: owners.clone() }; + let outcome = OutcomeReport::Categorical(42u16); + let winner_info = + OldWinnerInfo { outcome: outcome.clone(), outcome_info, is_finished: true }; + + crate::Winners::::insert(market_id, winner_info); + + ModifyGlobalDisputesStructures::::on_runtime_upgrade(); + + let possession = Some(Possession::Shared { owners }); + + let new_outcome_info = OutcomeInfo { outcome_sum, possession }; + + let expected = GDInfo { + winner_outcome: outcome, + outcome_info: new_outcome_info, + status: GDStatus::Finished, + }; + + let actual = crate::GlobalDisputesInfo::::get(market_id).unwrap(); + assert_eq!(expected, actual); + }); + } + + #[test] + fn on_runtime_sets_new_outcomes_storage_value_shared() { + ExtBuilder::default().build().execute_with(|| { + set_up_chain(); + + let outcome = OutcomeReport::Categorical(0u16); + let hash = + crate::Outcomes::::hashed_key_for::(0, outcome); + + let outcome_sum = 42u128.saturated_into::>(); + let owners = BoundedVec::try_from(vec![ALICE, BOB]).unwrap(); + + let outcome_info = OldOutcomeInfo { outcome_sum, owners: owners.clone() }; + + put_storage_value::>( + GLOBAL_DISPUTES, + GD_OUTCOMES, + &hash, + outcome_info, + ); + + ModifyGlobalDisputesStructures::::on_runtime_upgrade(); + + let possession = Some(Possession::Shared { owners }); + let expected = OutcomeInfo { outcome_sum, possession }; + + let actual = frame_support::migration::get_storage_value::>( + GLOBAL_DISPUTES, + GD_OUTCOMES, + &hash, + ) + .unwrap(); + assert_eq!(expected, actual); + }); + } + + #[test] + fn on_runtime_sets_new_outcomes_storage_value_paid() { + ExtBuilder::default().build().execute_with(|| { + set_up_chain(); + + let outcome = OutcomeReport::Categorical(0u16); + let hash = + crate::Outcomes::::hashed_key_for::(0, outcome); + + let outcome_sum = 42u128.saturated_into::>(); + let owners = BoundedVec::try_from(vec![ALICE]).unwrap(); + + let outcome_info = OldOutcomeInfo { outcome_sum, owners }; + + put_storage_value::>( + GLOBAL_DISPUTES, + GD_OUTCOMES, + &hash, + outcome_info, + ); + + ModifyGlobalDisputesStructures::::on_runtime_upgrade(); + + let possession = Some(Possession::Paid { owner: ALICE, fee: VotingOutcomeFee::get() }); + let expected = OutcomeInfo { outcome_sum, possession }; + + let actual = frame_support::migration::get_storage_value::>( + GLOBAL_DISPUTES, + GD_OUTCOMES, + &hash, + ) + .unwrap(); + assert_eq!(expected, actual); + }); + } + + #[test] + fn on_runtime_is_noop_if_versions_are_not_correct() { + ExtBuilder::default().build().execute_with(|| { + // storage migration already executed (storage version is incremented already) + StorageVersion::new(GD_NEXT_STORAGE_VERSION).put::>(); + + let outcome = OutcomeReport::Categorical(0u16); + let hash = + crate::Outcomes::::hashed_key_for::(0, outcome); + + let outcome_info = OldOutcomeInfo { + outcome_sum: 0u128.saturated_into::>(), + owners: BoundedVec::try_from(vec![ALICE]).unwrap(), + }; + + put_storage_value::>( + GLOBAL_DISPUTES, + GD_OUTCOMES, + &hash, + outcome_info, + ); + + ModifyGlobalDisputesStructures::::on_runtime_upgrade(); + + // no changes should have been made, because the storage version was already incremented + assert!( + get_storage_value::>(GLOBAL_DISPUTES, GD_OUTCOMES, &hash) + .is_none() + ); + }); + } + + fn set_up_chain() { + StorageVersion::new(GD_REQUIRED_STORAGE_VERSION).put::>(); + } +} diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index fdc11b1f5..8c562a2d6 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -35,7 +35,7 @@ pub struct OutcomeInfo { } /// The general information about the global dispute. -#[derive(TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] +#[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub struct GDInfo { /// The outcome, which is in the lead. pub winner_outcome: OutcomeReport, @@ -67,3 +67,32 @@ pub struct RewardInfo { pub reward: Balance, pub source: AccountId, } + +// TODO: to remove after the storage migration + +/// The information about a voting outcome of a global dispute. +#[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] +pub struct OldOutcomeInfo { + /// The current sum of all locks on this outcome. + pub outcome_sum: Balance, + /// The vector of owners of the outcome. + pub owners: OwnerInfo, +} + +/// The information about the current highest winning outcome. +#[derive(TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] +pub struct OldWinnerInfo { + /// The outcome, which is in the lead. + pub outcome: OutcomeReport, + /// The information about the winning outcome. + pub outcome_info: OldOutcomeInfo, + /// Check, if the global dispute is finished. + pub is_finished: bool, +} + +impl OldWinnerInfo { + pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { + let outcome_info = OldOutcomeInfo { outcome_sum: vote_sum, owners: Default::default() }; + OldWinnerInfo { outcome, is_finished: false, outcome_info } + } +} From 8af5060f979b0cb8bf8d886e8a3cf2445c921845 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 3 Jan 2023 13:31:50 +0100 Subject: [PATCH 009/368] correct frame executive --- runtime/common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index ef3d5383b..97f6bc1d1 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -71,7 +71,7 @@ macro_rules! decl_common_types { zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, >; - #[cfg(all(not(feature = "parachain"), not(feature = "with-global-disputes")))] + #[cfg(all(feature = "parachain", not(feature = "with-global-disputes")))] pub type Executive = frame_executive::Executive< Runtime, Block, From 549f170fa65970c87f6f3d6c0257fe76f5cfe14c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 3 Jan 2023 13:37:12 +0100 Subject: [PATCH 010/368] drain Winners storage item in gd --- zrml/global-disputes/src/migrations.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/migrations.rs b/zrml/global-disputes/src/migrations.rs index 4b3636eaa..33b32f35c 100644 --- a/zrml/global-disputes/src/migrations.rs +++ b/zrml/global-disputes/src/migrations.rs @@ -60,7 +60,7 @@ impl OnRuntimeUpgrade } log::info!("ModifyGlobalDisputesStructures: Starting..."); - for (market_id, winner_info) in crate::Winners::::iter() { + for (market_id, winner_info) in crate::Winners::::drain() { total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); let owners = winner_info.outcome_info.owners; @@ -180,6 +180,9 @@ impl OnRuntimeUpgrade let old_markets_count: u32 = Self::get_temp_storage(&old_counter_key).expect("old counter key storage not found"); assert_eq!(markets_count, old_markets_count); + + // empty Winners storage map + assert!(crate::Winners::::iter().next().is_none()); Ok(()) } } @@ -244,6 +247,8 @@ mod tests { let actual = crate::GlobalDisputesInfo::::get(market_id).unwrap(); assert_eq!(expected, actual); + + assert!(crate::Winners::::iter().next().is_none()); }); } @@ -278,6 +283,8 @@ mod tests { let actual = crate::GlobalDisputesInfo::::get(market_id).unwrap(); assert_eq!(expected, actual); + + assert!(crate::Winners::::iter().next().is_none()); }); } From 61ec1d21ab2b0e8a1369a50af2233fefad23e2d8 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 3 Jan 2023 15:14:35 +0100 Subject: [PATCH 011/368] admin_destroy_market destroys global dispute --- .../global-disputes/src/global_disputes_pallet_api.rs | 6 ++++++ zrml/global-disputes/src/lib.rs | 11 ++++++++++- zrml/prediction-markets/src/lib.rs | 5 +++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index e98f85cbe..da16df64b 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -74,6 +74,12 @@ pub trait GlobalDisputesPalletApi { /// - `market_id` - The id of the market. fn is_started(market_id: &MarketId) -> bool; + /// Check if global dispute is active. + /// + /// # Arguments + /// - `market_id` - The id of the market. + fn is_active(market_id: &MarketId) -> bool; + /// Check if a global dispute has not already been started. /// /// # Arguments diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index d04671e6a..7edfde535 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -715,7 +715,9 @@ mod pallet { initial_vote_balance: BalanceOf, ) -> DispatchResult { match >::get(market_id) { - Some(gd_info) if gd_info.status == GDStatus::Finished => { + Some(gd_info) + if matches!(gd_info.status, GDStatus::Finished | GDStatus::Destroyed) => + { return Err(Error::::GlobalDisputeAlreadyFinished.into()); } _ => (), @@ -784,6 +786,13 @@ mod pallet { >::get(market_id).is_some() } + fn is_active(market_id: &MarketIdOf) -> bool { + if let Some(gd_info) = >::get(market_id) { + return gd_info.status == GDStatus::Active; + } + false + } + fn destroy_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { >::try_mutate(market_id, |gd_info| { let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::NoGlobalDisputeStarted)?; diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 0898d05d8..b8528e05d 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -150,6 +150,11 @@ mod pallet { slash_market_creator(T::OracleBond::get()); } + #[cfg(feature = "with-global-disputes")] + if T::GlobalDisputes::is_active(&market_id) { + T::GlobalDisputes::destroy_global_dispute(&market_id)?; + } + // NOTE: Currently we don't clean up outcome assets. // TODO(#792): Remove outcome assets for accounts! Delete "resolved" assets of `orml_tokens` with storage migration. T::AssetManager::slash( From 06be54037998b98338ee42226eb642e6bc15afdb Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 3 Jan 2023 15:20:03 +0100 Subject: [PATCH 012/368] cargo fmt --- zrml/global-disputes/src/global_disputes_pallet_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index da16df64b..88be0766c 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -75,7 +75,7 @@ pub trait GlobalDisputesPalletApi { fn is_started(market_id: &MarketId) -> bool; /// Check if global dispute is active. - /// + /// /// # Arguments /// - `market_id` - The id of the market. fn is_active(market_id: &MarketId) -> bool; From f5f116c336823e6c5ac89499c24c475d31473925 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 4 Jan 2023 15:09:48 +0100 Subject: [PATCH 013/368] delete comment --- zrml/global-disputes/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 7edfde535..024bf7fe3 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -261,8 +261,6 @@ mod pallet { let voting_outcome_fee = T::VotingOutcomeFee::get(); - // Self::push_vote_outcome(&market_id, outcome.clone(), &owner, voting_outcome_fee)?; - let reward_account = Self::reward_account(&market_id); T::Currency::transfer( From b14faafad72aa89502de65d85024b4c8e86d95d6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 4 Jan 2023 17:36:33 +0100 Subject: [PATCH 014/368] start a global dispute via private api --- docs/changelog_for_devs.md | 3 + zrml/global-disputes/README.md | 16 ++- zrml/global-disputes/src/benchmarks.rs | 73 ++++++++++- .../src/global_disputes_pallet_api.rs | 21 ++-- zrml/global-disputes/src/lib.rs | 116 +++++++++++------ zrml/global-disputes/src/tests.rs | 118 ++++++++++++++---- zrml/global-disputes/src/types.rs | 9 +- zrml/global-disputes/src/weights.rs | 77 +++++++----- zrml/prediction-markets/src/lib.rs | 10 +- zrml/prediction-markets/src/tests.rs | 4 +- 10 files changed, 326 insertions(+), 121 deletions(-) diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index 9f113d413..28a0a2a21 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -5,6 +5,9 @@ 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). +- Added new dispatchable function: + - `refund_vote_fees` - Return all vote funds and fees, when a global dispute + was destroyed. # v0.3.7 diff --git a/zrml/global-disputes/README.md b/zrml/global-disputes/README.md index 2a93c9b7d..b71285897 100644 --- a/zrml/global-disputes/README.md +++ b/zrml/global-disputes/README.md @@ -32,11 +32,21 @@ on which the market finally resolves. get their reward. Fails if the global dispute is not concluded yet. - `reward_outcome_owner` - Reward the collected fees to the owner(s) of a voting outcome. Fails if not all outcomes are already purged. +- `refund_vote_fees` - Return all vote funds and fees, when a global dispute was + destroyed. #### Private Pallet API -- `push_vote_outcome` - Start a global dispute, add an initial voting outcome - and vote on it. +- `push_vote_outcome` - Add an initial voting outcome and vote on it with + `initial_vote_balance`. +- `get_voting_outcome_info` - Get the information (`outcome_sum`, `owners`) of a + voting outcome. - `determine_voting_winner` - Determine the canonical voting outcome based on total locked tokens. -- `is_started` - Check if the global dispute started already. +- `does_exist` - Check if the global dispute does already exist. +- `is_active` - Check if the global dispute is active to get votes + (`vote_on_outcome`) and allow the addition of new vote outcomes with + `add_vote_outcome`. +- `start_global_dispute` - Start a global dispute. +- `destroy_global_dispute` - Allow the users to get their voting funds and fee + payments back. diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index e088b4eea..a3687a967 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -65,6 +65,11 @@ benchmarks! { // ensure that we get the worst case // to actually insert the new item at the end of the binary search let market_id: MarketIdOf = v.into(); + let market = market_mock::(); + for i in 0..=v { + T::MarketCommons::push_market(market.clone()).unwrap(); + } + let outcome = OutcomeReport::Scalar(0); let amount: BalanceOf = T::MinOutcomeVoteAmount::get().saturated_into(); deposit::(&caller); @@ -203,14 +208,15 @@ benchmarks! { // this happens in the case, that Outcomes is not none at the query time. let w in 1..T::MaxOwners::get(); - let mut owners = Vec::new(); + let mut owners: Vec> = Vec::new(); for i in 1..=w { - let owner = account("winners_owner", i, 0); + let owner: AccountIdOf = account("winners_owner", i, 0); owners.push(owner); } - let owners = BoundedVec::try_from(owners).unwrap(); - let possession = Some(Possession::Shared { owners }); - let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession }; + let owners: BoundedVec, T::MaxOwners> = BoundedVec::try_from(owners) + .unwrap(); + + let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession: None }; let gd_info = GDInfo { winner_outcome: OutcomeReport::Scalar(0), status: GDStatus::Active, @@ -309,6 +315,8 @@ benchmarks! { let o in 1..T::MaxOwners::get(); let market_id: MarketIdOf = 0u128.saturated_into(); + let market = market_mock::(); + T::MarketCommons::push_market(market).unwrap(); for i in 1..=k { let owner = account("outcomes_owner", i, 0); @@ -354,6 +362,61 @@ benchmarks! { assert!(>::iter_prefix(market_id).next().is_none()); assert_last_event::(Event::OutcomesFullyCleaned:: { market_id }.into()); } + + refund_vote_fees { + // RemoveKeysLimit - 2 to ensure that we actually fully clean and return at the end + let k in 1..(T::RemoveKeysLimit::get() - 2); + + let o in 1..T::MaxOwners::get(); + + let market_id: MarketIdOf = 0u128.saturated_into(); + let market = market_mock::(); + T::MarketCommons::push_market(market).unwrap(); + + for i in 1..=k { + let owner = account("outcomes_owner", i, 0); + GlobalDisputes::::push_vote_outcome( + &market_id, + OutcomeReport::Scalar(i.into()), + &owner, + 1_000_000_000u128.saturated_into(), + ) + .unwrap(); + } + + let mut owners = Vec::new(); + for i in 1..=o { + let owner = account("winners_owner", i, 0); + owners.push(owner); + } + let owners = BoundedVec::try_from(owners.clone()).unwrap(); + let winner_outcome = OutcomeReport::Scalar(0); + + let possession = Some(Possession::Shared { owners }); + let outcome_info = OutcomeInfo { + outcome_sum: 42u128.saturated_into(), + possession, + }; + >::insert(market_id, winner_outcome.clone(), outcome_info); + + let possession = Some(Possession::Shared { owners: Default::default() }); + let outcome_info = OutcomeInfo { + outcome_sum: 42u128.saturated_into(), + possession, + }; + let gd_info = GDInfo {winner_outcome, status: GDStatus::Destroyed, outcome_info}; + >::insert(market_id, gd_info); + + let caller: T::AccountId = whitelisted_caller(); + + let outcome = OutcomeReport::Scalar(20); + + deposit::(&caller); + }: _(RawOrigin::Signed(caller.clone()), market_id) + verify { + assert!(>::iter_prefix(market_id).next().is_none()); + assert_last_event::(Event::OutcomesFullyCleaned:: { market_id }.into()); + } } impl_benchmark_test_suite!( diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 88be0766c..1de975afd 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -68,26 +68,33 @@ pub trait GlobalDisputesPalletApi { /// Returns the winning outcome. fn determine_voting_winner(market_id: &MarketId) -> Option; - /// Check if global dispute started. + /// Check if global dispute already exists. /// /// # Arguments /// - `market_id` - The id of the market. - fn is_started(market_id: &MarketId) -> bool; + fn does_exist(market_id: &MarketId) -> bool; - /// Check if global dispute is active. + /// Check if global dispute is active or initialized. But not finished. + /// This call is useful to check if a global dispute is ready for a destruction. /// /// # Arguments /// - `market_id` - The id of the market. - fn is_active(market_id: &MarketId) -> bool; + fn is_unfinished(market_id: &MarketId) -> bool; - /// Check if a global dispute has not already been started. + /// Check if a global dispute does not exist. /// /// # Arguments /// - `market_id` - The id of the market. - fn is_not_started(market_id: &MarketId) -> bool { - !Self::is_started(market_id) + fn does_not_exist(market_id: &MarketId) -> bool { + !Self::does_exist(market_id) } + /// Start a global dispute. + /// + /// # Arguments + /// - `market_id` - The id of the market. + fn start_global_dispute(market_id: &MarketId) -> Result<(), DispatchError>; + /// Destroy a global dispute and allow to return all funds of the participants. /// /// # Arguments diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 024bf7fe3..636414fa9 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -54,7 +54,27 @@ mod pallet { use zeitgeist_primitives::types::OutcomeReport; use zrml_market_commons::MarketCommonsPalletApi; + pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; + pub(crate) type MarketCommonsBalanceOf = + as Currency<::AccountId>>::Balance; + pub(crate) type CurrencyOf = + <::MarketCommons as MarketCommonsPalletApi>::Currency; + pub(crate) type MarketIdOf = + <::MarketCommons as MarketCommonsPalletApi>::MarketId; + + pub type AccountIdOf = ::AccountId; + + pub type OutcomeInfoOf = OutcomeInfo, BalanceOf, OwnerInfoOf>; + pub type GDInfoOf = GDInfo, BalanceOf, OwnerInfoOf>; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; + pub(crate) type OwnerInfoOf = BoundedVec, ::MaxOwners>; + pub type LockInfoOf = + BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; + type RewardInfoOf = RewardInfo, AccountIdOf, BalanceOf>; + + // TODO: to remove after the storage migration + pub type WinnerInfoOf = OldWinnerInfo, OwnerInfoOf>; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -107,22 +127,6 @@ mod pallet { type WeightInfo: WeightInfoZeitgeist; } - pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; - pub(crate) type MarketIdOf = - <::MarketCommons as MarketCommonsPalletApi>::MarketId; - pub type AccountIdOf = ::AccountId; - - pub type OutcomeInfoOf = OutcomeInfo, BalanceOf, OwnerInfoOf>; - pub type GDInfoOf = GDInfo, BalanceOf, OwnerInfoOf>; - type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - pub(crate) type OwnerInfoOf = BoundedVec, ::MaxOwners>; - pub type LockInfoOf = - BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; - type RewardInfoOf = RewardInfo, AccountIdOf, BalanceOf>; - - // TODO: to remove after the storage migration - pub type WinnerInfoOf = OldWinnerInfo, OwnerInfoOf>; - #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); @@ -193,8 +197,8 @@ mod pallet { pub enum Error { /// Sender tried to vote with an amount below a defined minium. AmountTooLow, - /// The global dispute period is already over and the winner is determined. - GlobalDisputeAlreadyFinished, + /// To start a global dispute, at least two outcomes are required. + AtLeastTwoOutcomesRequired, /// The global dispute status is invalid for this operation. InvalidGlobalDisputeStatus, /// Sender does not have enough funds for the vote on an outcome. @@ -206,7 +210,9 @@ mod pallet { /// The amount in the reward pot is zero. NoFundsToReward, /// No global dispute present at the moment. - NoGlobalDisputeStarted, + NoGlobalDisputeInitialized, + /// The global dispute has to be in initialized state during the initial outcome setup. + NotInSetupMode, /// There is no owner information for this outcome. NoPossession, /// The voting outcome has been already added. @@ -251,7 +257,7 @@ mod pallet { ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeStarted)?; + .ok_or(Error::::NoGlobalDisputeInitialized)?; ensure!(gd_info.status == GDStatus::Active, Error::::InvalidGlobalDisputeStatus); ensure!( @@ -294,7 +300,10 @@ mod pallet { /// Complexity: `O(n)`, /// where `n` is the number of all existing outcomes for a global dispute. #[frame_support::transactional] - #[pallet::weight(5000)] + #[pallet::weight(T::WeightInfo::refund_vote_fees( + T::RemoveKeysLimit::get(), + T::MaxOwners::get() + ))] pub fn refund_vote_fees( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, @@ -302,7 +311,7 @@ mod pallet { ensure_signed(origin)?; let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeStarted)?; + .ok_or(Error::::NoGlobalDisputeInitialized)?; ensure!(gd_info.status == GDStatus::Destroyed, Error::::UnfinishedGlobalDispute); let mut owners_len = 0u32; @@ -334,7 +343,7 @@ mod pallet { } // weight for max owners, because we don't know - Ok((Some(T::WeightInfo::purge_outcomes(removed_keys_amount, owners_len))).into()) + Ok((Some(T::WeightInfo::refund_vote_fees(removed_keys_amount, owners_len))).into()) } /// Purge all outcomes to allow the winning outcome owner(s) to get their reward. @@ -360,7 +369,7 @@ mod pallet { ensure_signed(origin)?; let mut gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeStarted)?; + .ok_or(Error::::NoGlobalDisputeInitialized)?; ensure!(gd_info.status == GDStatus::Finished, Error::::UnfinishedGlobalDispute); let winning_outcome: Option> = @@ -422,7 +431,7 @@ mod pallet { ); let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeStarted)?; + .ok_or(Error::::NoGlobalDisputeInitialized)?; ensure!(gd_info.status == GDStatus::Finished, Error::::UnfinishedGlobalDispute); let reward_account = Self::reward_account(&market_id); @@ -476,7 +485,7 @@ mod pallet { ensure!(amount >= T::MinOutcomeVoteAmount::get(), Error::::AmountTooLow); let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeStarted)?; + .ok_or(Error::::NoGlobalDisputeInitialized)?; ensure!(gd_info.status == GDStatus::Active, Error::::InvalidGlobalDisputeStatus); let mut outcome_info = @@ -633,9 +642,10 @@ mod pallet { >::mutate(market_id, |highest: &mut Option>| { *highest = Some(highest.clone().map_or( GDInfo::new(outcome.clone(), amount), - |prev_gd_info| { + |mut prev_gd_info| { if amount >= prev_gd_info.outcome_info.outcome_sum { - GDInfo::new(outcome.clone(), amount) + prev_gd_info.update_winner(outcome.clone(), amount); + prev_gd_info } else { prev_gd_info } @@ -712,14 +722,13 @@ mod pallet { owner: &T::AccountId, initial_vote_balance: BalanceOf, ) -> DispatchResult { - match >::get(market_id) { - Some(gd_info) - if matches!(gd_info.status, GDStatus::Finished | GDStatus::Destroyed) => - { - return Err(Error::::GlobalDisputeAlreadyFinished.into()); - } - _ => (), + let market = T::MarketCommons::market(market_id)?; + ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); + + if let Some(gd_info) = >::get(market_id) { + ensure!(gd_info.status == GDStatus::Initialized, Error::::NotInSetupMode); } + match >::get(market_id, &outcome) { Some(mut outcome_info) => { let outcome_sum = outcome_info.outcome_sum.saturating_add(initial_vote_balance); @@ -780,20 +789,40 @@ mod pallet { } } - fn is_started(market_id: &MarketIdOf) -> bool { + fn does_exist(market_id: &MarketIdOf) -> bool { >::get(market_id).is_some() } - fn is_active(market_id: &MarketIdOf) -> bool { + fn is_unfinished(market_id: &MarketIdOf) -> bool { if let Some(gd_info) = >::get(market_id) { - return gd_info.status == GDStatus::Active; + return gd_info.status == GDStatus::Active + || gd_info.status == GDStatus::Initialized; } false } + fn start_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { + T::MarketCommons::market(market_id)?; + + let mut iter = >::iter_prefix(market_id); + ensure!( + iter.next().is_some() && iter.next().is_some(), + Error::::AtLeastTwoOutcomesRequired + ); + + >::try_mutate(market_id, |gd_info| { + let mut raw_gd_info = + gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; + raw_gd_info.status = GDStatus::Active; + *gd_info = Some(raw_gd_info.clone()); + Ok(()) + }) + } + fn destroy_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { >::try_mutate(market_id, |gd_info| { - let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::NoGlobalDisputeStarted)?; + let mut raw_gd_info = + gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; raw_gd_info.status = GDStatus::Destroyed; *gd_info = Some(raw_gd_info.clone()); Ok(()) @@ -803,8 +832,12 @@ mod pallet { } #[cfg(any(feature = "runtime-benchmarks", test))] -pub(crate) fn market_mock() --> zeitgeist_primitives::types::Market> +pub(crate) fn market_mock() -> zeitgeist_primitives::types::Market< + T::AccountId, + MarketCommonsBalanceOf, + T::BlockNumber, + MomentOf, +> where T: crate::Config, { @@ -830,5 +863,6 @@ where resolved_outcome: None, scoring_rule: ScoringRule::CPMM, status: zeitgeist_primitives::types::MarketStatus::Disputed, + bonds: Default::default(), } } diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index c4713136f..6b706ca83 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -78,21 +78,23 @@ fn add_vote_outcome_works() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); + + setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let free_balance_alice_before = Balances::free_balance(&ALICE); let free_balance_reward_account = Balances::free_balance(GlobalDisputes::reward_account(&market_id)); assert_ok!(GlobalDisputes::add_vote_outcome( Origin::signed(ALICE), market_id, - OutcomeReport::Scalar(20), + OutcomeReport::Scalar(80), )); System::assert_last_event( Event::::AddedVotingOutcome { market_id, owner: ALICE, - outcome: OutcomeReport::Scalar(20), + outcome: OutcomeReport::Scalar(80), } .into(), ); @@ -114,8 +116,10 @@ fn add_vote_outcome_fails_with_outcome_mismatch() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); + + setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + assert_noop!( GlobalDisputes::add_vote_outcome( Origin::signed(ALICE), @@ -131,13 +135,11 @@ fn add_vote_outcome_fails_with_outcome_mismatch() { fn add_vote_outcome_fails_with_non_existing_market() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); assert_noop!( GlobalDisputes::add_vote_outcome( Origin::signed(ALICE), market_id, - OutcomeReport::Scalar(20), + OutcomeReport::Scalar(80), ), MarketError::::MarketDoesNotExist ); @@ -156,7 +158,7 @@ fn add_vote_outcome_fails_if_no_global_dispute_present() { market_id, OutcomeReport::Scalar(20), ), - Error::::NoGlobalDisputeStarted + Error::::NoGlobalDisputeInitialized ); }); } @@ -188,8 +190,10 @@ fn add_vote_outcome_fails_if_outcome_already_exists() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); + + setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + >::insert( market_id, OutcomeReport::Scalar(20), @@ -215,13 +219,15 @@ fn add_vote_outcome_fails_if_balance_too_low() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); + + setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + assert_noop!( GlobalDisputes::add_vote_outcome( Origin::signed(POOR_PAUL), market_id, - OutcomeReport::Scalar(20), + OutcomeReport::Scalar(80), ), BalancesError::::InsufficientBalance ); @@ -378,14 +384,11 @@ fn reward_outcome_owner_works_for_one_owner() { fn vote_fails_if_amount_below_min_outcome_vote_amount() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(20), &ALICE, 20 * BASE) - .unwrap(); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(40), &ALICE, 30 * BASE) - .unwrap(); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(60), &ALICE, 40 * BASE) - .unwrap(); + let market = market_mock::(); + Markets::::insert(market_id, &market); + + setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_noop!( GlobalDisputes::vote_on_outcome( @@ -403,10 +406,12 @@ fn vote_fails_if_amount_below_min_outcome_vote_amount() { fn vote_fails_for_insufficient_funds() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(20), &ALICE, 20 * BASE) - .unwrap(); + let market = market_mock::(); + Markets::::insert(market_id, &market); + + setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + // Paul does not have 50 * BASE assert_noop!( GlobalDisputes::vote_on_outcome( @@ -424,8 +429,11 @@ fn vote_fails_for_insufficient_funds() { fn determine_voting_winner_sets_the_last_outcome_for_same_vote_balances_as_the_canonical_outcome() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -455,6 +463,8 @@ fn determine_voting_winner_sets_the_last_outcome_for_same_vote_balances_as_the_c 42 * BASE )); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + assert_eq!( &GlobalDisputes::determine_voting_winner(&market_id).unwrap(), &OutcomeReport::Scalar(60) @@ -469,8 +479,11 @@ fn determine_voting_winner_sets_the_last_outcome_for_same_vote_balances_as_the_c fn vote_on_outcome_check_event() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(EVE), @@ -495,6 +508,8 @@ fn vote_on_outcome_check_event() { fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); let disputor = &ALICE; let free_balance_disputor_before = Balances::free_balance(disputor); @@ -523,6 +538,8 @@ fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { ) .unwrap(); + GlobalDisputes::start_global_dispute(&market_id).unwrap(); + assert_noop!( GlobalDisputes::vote_on_outcome( Origin::signed(*disputor), @@ -553,9 +570,13 @@ fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { fn transfer_fails_with_fully_locked_balance() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let disputor = &ALICE; let free_balance_disputor_before = Balances::free_balance(disputor); let arbitrary_amount = 42 * BASE; @@ -585,8 +606,11 @@ fn transfer_fails_with_fully_locked_balance() { fn reserve_fails_with_fully_locked_balance() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); let disputor = &ALICE; let free_balance_disputor_before = Balances::free_balance(disputor); @@ -617,8 +641,11 @@ fn reserve_fails_with_fully_locked_balance() { fn determine_voting_winner_works_four_outcome_votes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -667,8 +694,11 @@ fn determine_voting_winner_works_four_outcome_votes() { fn determine_voting_winner_works_three_outcome_votes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -711,8 +741,11 @@ fn determine_voting_winner_works_three_outcome_votes() { fn determine_voting_winner_works_two_outcome_votes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -755,8 +788,11 @@ fn determine_voting_winner_works_two_outcome_votes() { fn determine_voting_winner_works_with_accumulated_votes_for_alice() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -813,8 +849,11 @@ fn determine_voting_winner_works_with_accumulated_votes_for_alice() { fn purge_outcomes_works() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -851,8 +890,11 @@ fn purge_outcomes_works() { fn unlock_clears_lock_info() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -875,6 +917,9 @@ fn unlock_clears_lock_info() { fn vote_fails_if_outcome_does_not_exist() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) .unwrap(); GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(20), &ALICE, 20 * BASE) @@ -884,6 +929,8 @@ fn vote_fails_if_outcome_does_not_exist() { GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(60), &ALICE, 40 * BASE) .unwrap(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + assert_noop!( GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -900,8 +947,11 @@ fn vote_fails_if_outcome_does_not_exist() { fn locking_works_for_one_market() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); setup_vote_outcomes_with_hundred(&market_id); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); assert_eq!(>::get(ALICE), vec![]); assert!(Balances::locks(ALICE).is_empty()); @@ -986,10 +1036,17 @@ fn locking_works_for_one_market() { fn locking_works_for_two_markets_with_stronger_first_unlock() { ExtBuilder::default().build().execute_with(|| { let market_id_1 = 0u128; + let market_1 = market_mock::(); + Markets::::insert(market_id_1, &market_1); + let market_id_2 = 1u128; + let market_2 = market_mock::(); + Markets::::insert(market_id_2, &market_2); setup_vote_outcomes_with_hundred(&market_id_1); setup_vote_outcomes_with_hundred(&market_id_2); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1)); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2)); assert_eq!(>::get(ALICE), vec![]); assert!(Balances::locks(ALICE).is_empty()); @@ -1076,10 +1133,17 @@ fn locking_works_for_two_markets_with_stronger_first_unlock() { fn locking_works_for_two_markets_with_weaker_first_unlock() { ExtBuilder::default().build().execute_with(|| { let market_id_1 = 0u128; + let market_1 = market_mock::(); + Markets::::insert(market_id_1, &market_1); + let market_id_2 = 1u128; + let market_2 = market_mock::(); + Markets::::insert(market_id_2, &market_2); setup_vote_outcomes_with_hundred(&market_id_1); setup_vote_outcomes_with_hundred(&market_id_2); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1)); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2)); assert_eq!(>::get(ALICE), vec![]); assert!(Balances::locks(ALICE).is_empty()); diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 8c562a2d6..3f75489d1 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -48,12 +48,19 @@ pub struct GDInfo { impl GDInfo { pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession: None }; - GDInfo { winner_outcome: outcome, status: GDStatus::Active, outcome_info } + GDInfo { winner_outcome: outcome, status: GDStatus::Initialized, outcome_info } + } + + pub fn update_winner(&mut self, outcome: OutcomeReport, vote_sum: Balance) { + self.winner_outcome = outcome; + self.outcome_info.outcome_sum = vote_sum; } } #[derive(TypeInfo, Debug, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub enum GDStatus { + /// The global dispute is initialized. + Initialized, /// The global dispute is in progress. Active, /// The global dispute is finished. diff --git a/zrml/global-disputes/src/weights.rs b/zrml/global-disputes/src/weights.rs index dc2aef1bb..e9554136b 100644 --- a/zrml/global-disputes/src/weights.rs +++ b/zrml/global-disputes/src/weights.rs @@ -18,11 +18,11 @@ //! Autogenerated weights for zrml_global_disputes //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-25, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-01-04, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/zeitgeist +// ./target/release/zeitgeist // benchmark // pallet // --chain=dev @@ -33,8 +33,8 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --template=./misc/weight_template.hbs // --output=./zrml/global-disputes/src/weights.rs +// --template=./misc/weight_template.hbs #![allow(unused_parens)] #![allow(unused_imports)] @@ -51,34 +51,35 @@ pub trait WeightInfoZeitgeist { fn add_vote_outcome(w: u32) -> Weight; fn reward_outcome_owner_with_funds(o: u32) -> Weight; fn purge_outcomes(k: u32, o: u32) -> Weight; + fn refund_vote_fees(k: u32, o: u32) -> Weight; } /// Weight functions for zrml_global_disputes (automatically generated) pub struct WeightInfo(PhantomData); impl WeightInfoZeitgeist for WeightInfo { - // Storage: GlobalDisputes Winners (r:1 w:1) + // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:1) // Storage: GlobalDisputes Outcomes (r:1 w:1) // Storage: GlobalDisputes Locks (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn vote_on_outcome(o: u32, v: u32) -> Weight { - (77_234_000 as Weight) + (48_823_000 as Weight) // Standard Error: 4_000 - .saturating_add((122_000 as Weight).saturating_mul(o as Weight)) + .saturating_add((309_000 as Weight).saturating_mul(o as Weight)) // Standard Error: 0 - .saturating_add((108_000 as Weight).saturating_mul(v as Weight)) + .saturating_add((55_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } // Storage: GlobalDisputes Locks (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:0) - // Storage: GlobalDisputes Winners (r:5 w:0) + // Storage: GlobalDisputes GlobalDisputesInfo (r:5 w:0) fn unlock_vote_balance_set(l: u32, o: u32) -> Weight { - (21_110_000 as Weight) - // Standard Error: 3_000 - .saturating_add((6_327_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 20_000 - .saturating_add((1_976_000 as Weight).saturating_mul(o as Weight)) + (25_163_000 as Weight) + // Standard Error: 2_000 + .saturating_add((4_239_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 13_000 + .saturating_add((613_000 as Weight).saturating_mul(o as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(l as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) @@ -86,46 +87,60 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: GlobalDisputes Locks (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:0) - // Storage: GlobalDisputes Winners (r:5 w:0) + // Storage: GlobalDisputes GlobalDisputesInfo (r:5 w:0) fn unlock_vote_balance_remove(l: u32, o: u32) -> Weight { - (18_940_000 as Weight) - // Standard Error: 3_000 - .saturating_add((6_247_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 19_000 - .saturating_add((1_822_000 as Weight).saturating_mul(o as Weight)) + (19_407_000 as Weight) + // Standard Error: 1_000 + .saturating_add((4_127_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 8_000 + .saturating_add((681_000 as Weight).saturating_mul(o as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(l as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - // Storage: GlobalDisputes Winners (r:1 w:1) + // Storage: MarketCommons Markets (r:1 w:0) + // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:1) // Storage: GlobalDisputes Outcomes (r:1 w:1) // Storage: System Account (r:1 w:1) - fn add_vote_outcome(_w: u32) -> Weight { - (91_053_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) + fn add_vote_outcome(w: u32) -> Weight { + (61_676_000 as Weight) + // Standard Error: 5_000 + .saturating_add((68_000 as Weight).saturating_mul(w as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: GlobalDisputes Outcomes (r:1 w:0) - // Storage: GlobalDisputes Winners (r:1 w:0) + // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:0) // Storage: System Account (r:2 w:2) fn reward_outcome_owner_with_funds(o: u32) -> Weight { - (51_857_000 as Weight) - // Standard Error: 27_000 - .saturating_add((33_805_000 as Weight).saturating_mul(o as Weight)) + (36_443_000 as Weight) + // Standard Error: 24_000 + .saturating_add((24_275_000 as Weight).saturating_mul(o as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(o as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(o as Weight))) } - // Storage: GlobalDisputes Winners (r:1 w:1) + // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:1) // Storage: GlobalDisputes Outcomes (r:3 w:2) fn purge_outcomes(k: u32, _o: u32) -> Weight { - (0 as Weight) - // Standard Error: 11_000 - .saturating_add((23_452_000 as Weight).saturating_mul(k as Weight)) + (83_016_000 as Weight) + // Standard Error: 4_000 + .saturating_add((13_338_000 as Weight).saturating_mul(k as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } + // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:0) + // Storage: GlobalDisputes Outcomes (r:3 w:2) + fn refund_vote_fees(k: u32, _o: u32) -> Weight { + (31_076_000 as Weight) + // Standard Error: 4_000 + .saturating_add((13_543_000 as Weight).saturating_mul(k as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) + } } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index c01e16c7a..b8db6a24d 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -244,7 +244,7 @@ mod pallet { } #[cfg(feature = "with-global-disputes")] - if T::GlobalDisputes::is_active(&market_id) { + if T::GlobalDisputes::is_unfinished(&market_id) { T::GlobalDisputes::destroy_global_dispute(&market_id)?; } @@ -1350,8 +1350,8 @@ mod pallet { ); ensure!( - T::GlobalDisputes::is_not_started(&market_id), - Error::::GlobalDisputeAlreadyStarted + T::GlobalDisputes::does_not_exist(&market_id), + Error::::GlobalDisputeExistsAlready ); // add report outcome to voting choices @@ -1374,6 +1374,8 @@ mod pallet { )?; } + T::GlobalDisputes::start_global_dispute(&market_id)?; + // ensure, that global disputes controls the resolution now // it does not end after the dispute period now, but after the global dispute end Self::remove_last_dispute_from_market_ids_per_dispute_block(&disputes, &market_id)?; @@ -1670,7 +1672,7 @@ mod pallet { /// The weights length has to be equal to the assets length. WeightsLenMustEqualAssetsLen, /// The start of the global dispute for this market happened already. - GlobalDisputeAlreadyStarted, + GlobalDisputeExistsAlready, } #[pallet::event] diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 7848524dc..a3e8be9aa 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -2415,12 +2415,12 @@ fn start_global_dispute_works() { now + ::GlobalDisputePeriod::get(), ); assert_eq!(market_ids, vec![market_id]); - assert!(GlobalDisputes::is_started(&market_id)); + assert!(GlobalDisputes::is_unfinished(&market_id)); System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); assert_noop!( PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), - Error::::GlobalDisputeAlreadyStarted + Error::::GlobalDisputeExistsAlready ); } }); From 8c0f2ed48e871960ad4a2c07b94868408d1b2b4b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 9 Jan 2023 15:58:32 +0100 Subject: [PATCH 015/368] move disputes to simple-disputes --- Cargo.lock | 1 + zrml/prediction-markets/src/lib.rs | 79 +------------ zrml/simple-disputes/Cargo.toml | 1 + zrml/simple-disputes/src/lib.rs | 184 +++++++++++++++++++++++++---- 4 files changed, 167 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9d02c5c5..c19c8d2e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12996,6 +12996,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "orml-traits", "pallet-balances", "pallet-timestamp", "parity-scale-codec", diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 045ec7e0c..8ee475df1 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -534,24 +534,13 @@ mod pallet { pub fn dispute( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, - outcome: OutcomeReport, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let disputes = Disputes::::get(market_id); + let curr_block_num = >::block_number(); let market = >::market(&market_id)?; - ensure!( - matches!(market.status, MarketStatus::Reported | MarketStatus::Disputed), - Error::::InvalidMarketStatus - ); - let num_disputes: u32 = disputes.len().saturated_into(); - Self::validate_dispute(&disputes, &market, num_disputes, &outcome)?; - T::AssetManager::reserve_named( - &Self::reserve_id(), - Asset::Ztg, - &who, - default_dispute_bond::(disputes.len()), - )?; + ensure!(market.status == MarketStatus::Reported, Error::::InvalidMarketStatus); + match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_dispute(&disputes, &market_id, &market)? @@ -563,12 +552,10 @@ mod pallet { T::SimpleDisputes::on_dispute(&disputes, &market_id, &market)? } } + Self::remove_last_dispute_from_market_ids_per_dispute_block(&disputes, &market_id)?; Self::set_market_as_disputed(&market, &market_id)?; - let market_dispute = MarketDispute { at: curr_block_num, by: who, outcome }; - >::try_mutate(market_id, |disputes| { - disputes.try_push(market_dispute.clone()).map_err(|_| >::StorageOverflow) - })?; + // each dispute resets dispute_duration let dispute_duration_ends_at_block = curr_block_num.saturating_add(market.deadlines.dispute_duration); @@ -579,7 +566,6 @@ mod pallet { Self::deposit_event(Event::MarketDisputed( market_id, MarketStatus::Disputed, - market_dispute, )); // TODO(#782): add court benchmark Ok((Some(T::WeightInfo::dispute_authorized(num_disputes, CacheSize::get()))).into()) @@ -1441,15 +1427,6 @@ mod pallet { /// The origin that is allowed to destroy markets. type DestroyOrigin: EnsureOrigin; - /// The base amount of currency that must be bonded in order to create a dispute. - #[pallet::constant] - type DisputeBond: Get>; - - /// The additional amount of currency that must be bonded when creating a subsequent - /// dispute. - #[pallet::constant] - type DisputeFactor: Get>; - /// Event type Event: From> + IsType<::Event>; @@ -1692,7 +1669,7 @@ mod pallet { /// A market has been closed \[market_id\] MarketClosed(MarketIdOf), /// A market has been disputed \[market_id, new_market_status, new_outcome\] - MarketDisputed(MarketIdOf, MarketStatus, MarketDispute), + MarketDisputed(MarketIdOf, MarketStatus), /// An advised market has ended before it was approved or rejected. \[market_id\] MarketExpired(MarketIdOf), /// A pending market has been rejected as invalid with a reason. \[market_id, reject_reason\] @@ -1837,17 +1814,6 @@ mod pallet { #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); - /// For each market, this holds the dispute information for each dispute that's - /// been issued. - #[pallet::storage] - pub type Disputes = StorageMap< - _, - Blake2_128Concat, - MarketIdOf, - BoundedVec, T::MaxDisputes>, - ValueQuery, - >; - #[pallet::storage] pub type MarketIdsPerOpenBlock = StorageMap< _, @@ -2149,26 +2115,6 @@ mod pallet { } } - fn ensure_can_not_dispute_the_same_outcome( - disputes: &[MarketDispute], - report: &Report, - outcome: &OutcomeReport, - ) -> DispatchResult { - if let Some(last_dispute) = disputes.last() { - ensure!(&last_dispute.outcome != outcome, Error::::CannotDisputeSameOutcome); - } else { - ensure!(&report.outcome != outcome, Error::::CannotDisputeSameOutcome); - } - - Ok(()) - } - - #[inline] - fn ensure_disputes_does_not_exceed_max_disputes(num_disputes: u32) -> DispatchResult { - ensure!(num_disputes < T::MaxDisputes::get(), Error::::MaxDisputesReached); - Ok(()) - } - fn ensure_market_is_active(market: &MarketOf) -> DispatchResult { ensure!(market.status == MarketStatus::Active, Error::::MarketIsNotActive); Ok(()) @@ -2806,19 +2752,6 @@ mod pallet { Ok(T::WeightInfo::start_subsidy(total_assets.saturated_into())) } - fn validate_dispute( - disputes: &[MarketDispute], - market: &MarketOf, - num_disputes: u32, - outcome_report: &OutcomeReport, - ) -> DispatchResult { - let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; - ensure!(market.matches_outcome_report(outcome_report), Error::::OutcomeMismatch); - Self::ensure_can_not_dispute_the_same_outcome(disputes, report, outcome_report)?; - Self::ensure_disputes_does_not_exceed_max_disputes(num_disputes)?; - Ok(()) - } - fn construct_market( creator: T::AccountId, creator_fee: u8, diff --git a/zrml/simple-disputes/Cargo.toml b/zrml/simple-disputes/Cargo.toml index ee00bd310..11629771a 100644 --- a/zrml/simple-disputes/Cargo.toml +++ b/zrml/simple-disputes/Cargo.toml @@ -7,6 +7,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" sp-runtime = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/paritytech/substrate" } zeitgeist-primitives = { default-features = false, path = "../../primitives" } zrml-market-commons = { default-features = false, path = "../market-commons" } +orml-traits = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library" } [dev-dependencies] pallet-balances = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 2609fe844..37612e807 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -31,51 +31,101 @@ pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; mod pallet { use crate::SimpleDisputesPalletApi; use core::marker::PhantomData; + use frame_system::pallet_prelude::*; use frame_support::{ dispatch::DispatchResult, traits::{Currency, Get, Hooks, IsType}, PalletId, + pallet_prelude::*, + transactional, }; use sp_runtime::DispatchError; use zeitgeist_primitives::{ traits::DisputeApi, - types::{Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport}, + types::{Report, Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport}, }; + use sp_runtime::traits::Saturating; + use sp_runtime::SaturatedConversion; + use zeitgeist_primitives::traits::ZeitgeistAssetManager; use zrml_market_commons::MarketCommonsPalletApi; - - type BalanceOf = - as Currency<::AccountId>>::Balance; - pub(crate) type CurrencyOf = - <::MarketCommons as MarketCommonsPalletApi>::Currency; - pub(crate) type MarketIdOf = - <::MarketCommons as MarketCommonsPalletApi>::MarketId; - pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; - pub(crate) type MarketOf = Market< - ::AccountId, - BalanceOf, - ::BlockNumber, - MomentOf, - >; - - #[pallet::call] - impl Pallet {} + use orml_traits::currency::NamedMultiReservableCurrency; #[pallet::config] pub trait Config: frame_system::Config { + /// Shares of outcome assets and native currency + type AssetManager: ZeitgeistAssetManager< + Self::AccountId, + Balance = as Currency>::Balance, + CurrencyId = Asset>, + ReserveIdentifier = [u8; 8], + >; + /// Event type Event: From> + IsType<::Event>; + /// The base amount of currency that must be bonded in order to create a dispute. + #[pallet::constant] + type DisputeBond: Get>; + + /// The additional amount of currency that must be bonded when creating a subsequent + /// dispute. + #[pallet::constant] + type DisputeFactor: Get>; + /// The identifier of individual markets. type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, >; + /// The maximum number of disputes allowed on any single market. + #[pallet::constant] + type MaxDisputes: Get; + /// The pallet identifier. #[pallet::constant] type PalletId: Get; + + #[pallet::constant] + type PredictionMarketsPalletId: Get; } + type BalanceOf = + as Currency<::AccountId>>::Balance; + pub(crate) type CurrencyOf = + <::MarketCommons as MarketCommonsPalletApi>::Currency; + pub(crate) type MarketIdOf = + <::MarketCommons as MarketCommonsPalletApi>::MarketId; + pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; + pub(crate) type MarketOf = Market< + ::AccountId, + BalanceOf, + ::BlockNumber, + MomentOf, + >; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + /// For each market, this holds the dispute information for each dispute that's + /// been issued. + #[pallet::storage] + pub type Disputes = StorageMap< + _, + Blake2_128Concat, + MarketIdOf, + BoundedVec, T::MaxDisputes>, + ValueQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(fn deposit_event)] + pub enum Event + where + T: Config, { + OutcomeReserved { market_id: MarketIdOf, dispute: MarketDispute }, + } + #[pallet::error] pub enum Error { /// 1. Any resolution must either have a `Disputed` or `Reported` market status @@ -83,16 +133,93 @@ mod pallet { InvalidMarketStatus, /// On dispute or resolution, someone tried to pass a non-simple-disputes market type MarketDoesNotHaveSimpleDisputesMechanism, + StorageOverflow, + OutcomeMismatch, + CannotDisputeSameOutcome, + MarketIsNotReported, + MaxDisputesReached, } - #[pallet::event] - pub enum Event - where - T: Config, {} - #[pallet::hooks] impl Hooks for Pallet {} + #[pallet::call] + impl Pallet { + #[pallet::weight(5000)] + #[transactional] + pub fn reserve_outcome( + origin: OriginFor, + #[pallet::compact] market_id: MarketIdOf, + outcome: OutcomeReport, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let market = T::MarketCommons::market(&market_id)?; + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, + Error::::MarketDoesNotHaveSimpleDisputesMechanism + ); + ensure!( + market.status == MarketStatus::Disputed, + Error::::InvalidMarketStatus + ); + ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); + let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; + + let now = >::block_number(); + let disputes = Disputes::::get(&market_id); + let num_disputes: u32 = disputes.len().saturated_into(); + + Self::ensure_can_not_dispute_the_same_outcome(&disputes, report, &outcome)?; + Self::ensure_disputes_does_not_exceed_max_disputes(num_disputes)?; + + T::AssetManager::reserve_named( + &Self::reserve_id(), + Asset::Ztg, + &who, + default_dispute_bond::(disputes.len()), + )?; + + let market_dispute = MarketDispute { at: now, by: who, outcome }; + >::try_mutate(market_id, |disputes| { + disputes.try_push(market_dispute.clone()).map_err(|_| >::StorageOverflow) + })?; + + Self::deposit_event(Event::OutcomeReserved { + market_id, + dispute: market_dispute, + }); + + Ok((Some(5000)).into()) + } + } + + impl Pallet { + #[inline] + pub fn reserve_id() -> [u8; 8] { + T::PredictionMarketsPalletId::get().0 + } + + fn ensure_can_not_dispute_the_same_outcome( + disputes: &[MarketDispute], + report: &Report, + outcome: &OutcomeReport, + ) -> DispatchResult { + if let Some(last_dispute) = disputes.last() { + ensure!(&last_dispute.outcome != outcome, Error::::CannotDisputeSameOutcome); + } else { + ensure!(&report.outcome != outcome, Error::::CannotDisputeSameOutcome); + } + + Ok(()) + } + + #[inline] + fn ensure_disputes_does_not_exceed_max_disputes(num_disputes: u32) -> DispatchResult { + ensure!(num_disputes < T::MaxDisputes::get(), Error::::MaxDisputesReached); + Ok(()) + } + } + impl DisputeApi for Pallet where T: Config, @@ -137,6 +264,13 @@ mod pallet { impl SimpleDisputesPalletApi for Pallet where T: Config {} - #[pallet::pallet] - pub struct Pallet(PhantomData); + // No-one can bound more than BalanceOf, therefore, this functions saturates + pub(crate) fn default_dispute_bond(n: usize) -> BalanceOf + where + T: Config, + { + T::DisputeBond::get().saturating_add( + T::DisputeFactor::get().saturating_mul(n.saturated_into::().into()), + ) + } } From 1977d2ac69fc8e1823eed2700c686269eaae79e1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 11 Jan 2023 14:47:53 +0100 Subject: [PATCH 016/368] move more to simple-disputes --- primitives/src/traits/dispute_api.rs | 9 +- zrml/prediction-markets/src/lib.rs | 73 +++----------- zrml/simple-disputes/src/lib.rs | 138 +++++++++++++++++---------- 3 files changed, 105 insertions(+), 115 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 17d062692..a332874c9 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -40,11 +40,7 @@ pub trait DisputeApi { /// /// Further interaction with the dispute API (if necessary) **should** happen through an /// associated pallet. **May** assume that `market.dispute_mechanism` refers to the calling dispute API. - fn on_dispute( - previous_disputes: &[MarketDispute], - market_id: &Self::MarketId, - market: &MarketOfDisputeApi, - ) -> DispatchResult; + fn on_dispute(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; /// Manage market resolution of a disputed market. /// @@ -56,7 +52,6 @@ pub trait DisputeApi { /// Returns the dispute mechanism's report if available, otherwise `None`. If `None` is /// returned, this means that the dispute could not be resolved. fn on_resolution( - disputes: &[MarketDispute], market_id: &Self::MarketId, market: &MarketOfDisputeApi, ) -> Result, DispatchError>; @@ -68,7 +63,6 @@ pub trait DisputeApi { /// /// Returns the future resolution block if available, otherwise `None`. fn get_auto_resolve( - disputes: &[MarketDispute], market_id: &Self::MarketId, market: &MarketOfDisputeApi, ) -> Result, DispatchError>; @@ -77,7 +71,6 @@ pub trait DisputeApi { /// was unable to come to a conclusion. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. fn has_failed( - disputes: &[MarketDispute], market_id: &Self::MarketId, market: &MarketOfDisputeApi, ) -> Result; diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 3602be26f..7b009462b 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -521,7 +521,7 @@ mod pallet { #[pallet::compact] market_id: MarketIdOf, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - + let curr_block_num = >::block_number(); let market = >::market(&market_id)?; ensure!(market.status == MarketStatus::Reported, Error::::InvalidMarketStatus); @@ -529,22 +529,18 @@ mod pallet { // TODO(#782): use multiple benchmarks paths for different dispute mechanisms match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::on_dispute(&disputes, &market_id, &market)? - } - MarketDisputeMechanism::Court => { - T::Court::on_dispute(&disputes, &market_id, &market)? + T::Authorized::on_dispute(&market_id, &market)? } + MarketDisputeMechanism::Court => T::Court::on_dispute(&market_id, &market)?, MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_dispute(&disputes, &market_id, &market)? + T::SimpleDisputes::on_dispute(&market_id, &market)? } } Self::set_market_as_disputed(&market, &market_id)?; - let market_dispute = MarketDispute { at: curr_block_num, by: who, outcome }; - Self::deposit_event(Event::MarketDisputed( - market_id, - MarketStatus::Disputed, - )); + + Self::deposit_event(Event::MarketDisputed(market_id, MarketStatus::Disputed)); + Ok((Some(T::WeightInfo::dispute_authorized())).into()) } @@ -2003,13 +1999,13 @@ mod pallet { // TODO(#782): use multiple benchmarks paths for different dispute mechanisms let auto_resolve_block_opt = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::get_auto_resolve(&disputes, market_id, &market)? + T::Authorized::get_auto_resolve(market_id, &market)? } MarketDisputeMechanism::Court => { - T::Court::get_auto_resolve(&disputes, market_id, &market)? + T::Court::get_auto_resolve(market_id, &market)? } MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::get_auto_resolve(&disputes, market_id, &market)? + T::SimpleDisputes::get_auto_resolve(market_id, &market)? } }; if let Some(auto_resolve_block) = auto_resolve_block_opt { @@ -2290,8 +2286,6 @@ mod pallet { market: &MarketOf, ) -> Result { let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; - let disputes = Disputes::::get(market_id); - let mut resolved_outcome_option = None; #[cfg(feature = "with-global-disputes")] @@ -2306,13 +2300,11 @@ mod pallet { if resolved_outcome_option.is_none() { resolved_outcome_option = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::on_resolution(&disputes, market_id, market)? - } - MarketDisputeMechanism::Court => { - T::Court::on_resolution(&disputes, market_id, market)? + T::Authorized::on_resolution(market_id, market)? } + MarketDisputeMechanism::Court => T::Court::on_resolution(market_id, market)?, MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_resolution(&disputes, market_id, market)? + T::SimpleDisputes::on_resolution(market_id, market)? } }; } @@ -2320,53 +2312,16 @@ mod pallet { let resolved_outcome = resolved_outcome_option.unwrap_or_else(|| report.outcome.clone()); - let mut correct_reporters: Vec = Vec::new(); - // If the oracle reported right, return the OracleBond, otherwise slash it to // pay the correct reporters. - let mut overall_imbalance = NegativeImbalanceOf::::zero(); if report.by == market.oracle && report.outcome == resolved_outcome { Self::unreserve_oracle_bond(market_id)?; } else { let imbalance = Self::slash_oracle_bond(market_id, None)?; + // TODO what should be done with the OracleBond imbalance? overall_imbalance.subsume(imbalance); } - for (i, dispute) in disputes.iter().enumerate() { - let actual_bond = default_dispute_bond::(i); - if dispute.outcome == resolved_outcome { - T::AssetManager::unreserve_named( - &Self::reserve_id(), - Asset::Ztg, - &dispute.by, - actual_bond, - ); - - correct_reporters.push(dispute.by.clone()); - } else { - let (imbalance, _) = CurrencyOf::::slash_reserved_named( - &Self::reserve_id(), - &dispute.by, - actual_bond.saturated_into::().saturated_into(), - ); - overall_imbalance.subsume(imbalance); - } - } - - // Fold all the imbalances into one and reward the correct reporters. The - // number of correct reporters might be zero if the market defaults to the - // report after abandoned dispute. In that case, the rewards remain slashed. - if let Some(reward_per_each) = - overall_imbalance.peek().checked_div(&correct_reporters.len().saturated_into()) - { - for correct_reporter in &correct_reporters { - let (actual_reward, leftover) = overall_imbalance.split(reward_per_each); - overall_imbalance = leftover; - CurrencyOf::::resolve_creating(correct_reporter, actual_reward); - } - } - T::Slash::on_unbalanced(overall_imbalance); - Ok(resolved_outcome) } diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index acd23bad6..0b19c7c46 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -31,25 +31,27 @@ pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; mod pallet { use crate::SimpleDisputesPalletApi; use core::marker::PhantomData; - use frame_system::pallet_prelude::*; use frame_support::{ dispatch::DispatchResult, ensure, - traits::{Currency, Get, Hooks, IsType}, - PalletId, pallet_prelude::*, - transactional, + traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency, OnUnbalanced}, + transactional, PalletId, + }; + use frame_system::pallet_prelude::*; + use orml_traits::currency::NamedMultiReservableCurrency; + use sp_runtime::{ + traits::{CheckedDiv, Saturating}, + DispatchError, SaturatedConversion, }; - use sp_runtime::{traits::Saturating, DispatchError}; use zeitgeist_primitives::{ - traits::{DisputeApi, DisputeResolutionApi}, - types::{Report, Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport}, + traits::{DisputeApi, DisputeResolutionApi, ZeitgeistAssetManager}, + types::{ + Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport, + Report, + }, }; - use sp_runtime::traits::Saturating; - use sp_runtime::SaturatedConversion; - use zeitgeist_primitives::traits::ZeitgeistAssetManager; use zrml_market_commons::MarketCommonsPalletApi; - use orml_traits::currency::NamedMultiReservableCurrency; #[pallet::config] pub trait Config: frame_system::Config { @@ -96,12 +98,17 @@ mod pallet { #[pallet::constant] type PredictionMarketsPalletId: Get; + + /// Handler for slashed funds. + type Slash: OnUnbalanced>; } type BalanceOf = - as Currency<::AccountId>>::Balance; + as Currency<::AccountId>>::Balance; pub(crate) type CurrencyOf = <::MarketCommons as MarketCommonsPalletApi>::Currency; + pub(crate) type NegativeImbalanceOf = + as Currency<::AccountId>>::NegativeImbalance; pub(crate) type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; @@ -130,9 +137,13 @@ mod pallet { #[pallet::generate_deposit(fn deposit_event)] pub enum Event where - T: Config, { - OutcomeReserved { market_id: MarketIdOf, dispute: MarketDispute }, - } + T: Config, + { + OutcomeReserved { + market_id: MarketIdOf, + dispute: MarketDispute, + }, + } #[pallet::error] pub enum Error { @@ -166,10 +177,7 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - ensure!( - market.status == MarketStatus::Disputed, - Error::::InvalidMarketStatus - ); + ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; @@ -192,10 +200,13 @@ mod pallet { disputes.try_push(market_dispute.clone()).map_err(|_| >::StorageOverflow) })?; - Self::deposit_event(Event::OutcomeReserved { - market_id, - dispute: market_dispute, - }); + // each dispute resets dispute_duration + Self::remove_auto_resolve(disputes.as_slice(), &market_id, &market); + let dispute_duration_ends_at_block = + now.saturating_add(market.deadlines.dispute_duration); + T::DisputeResolution::add_auto_resolve(&market_id, dispute_duration_ends_at_block)?; + + Self::deposit_event(Event::OutcomeReserved { market_id, dispute: market_dispute }); Ok((Some(5000)).into()) } @@ -261,27 +272,16 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute( - disputes: &[MarketDispute], - market_id: &Self::MarketId, - market: &MarketOf, - ) -> DispatchResult { + fn on_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - Self::remove_auto_resolve(disputes, market_id, market); - let curr_block_num = >::block_number(); - // each dispute resets dispute_duration - let dispute_duration_ends_at_block = - curr_block_num.saturating_add(market.deadlines.dispute_duration); - T::DisputeResolution::add_auto_resolve(market_id, dispute_duration_ends_at_block)?; Ok(()) } fn on_resolution( - disputes: &[MarketDispute], - _: &Self::MarketId, + market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { ensure!( @@ -290,30 +290,72 @@ mod pallet { ); ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); - if let Some(last_dispute) = disputes.last() { - Ok(Some(last_dispute.outcome.clone())) - } else { - Err(Error::::InvalidMarketStatus.into()) + let disputes = Disputes::::get(market_id); + + let last_dispute = match disputes.last() { + Some(l) => l, + // if there are no disputes, then the market is resolved with the default report + None => return Ok(None), + }; + + let resolved_outcome = last_dispute.outcome.clone(); + + let mut correct_reporters: Vec = Vec::new(); + + let mut overall_imbalance = NegativeImbalanceOf::::zero(); + + for (i, dispute) in disputes.iter().enumerate() { + let actual_bond = default_dispute_bond::(i); + if dispute.outcome == resolved_outcome { + T::AssetManager::unreserve_named( + &Self::reserve_id(), + Asset::Ztg, + &dispute.by, + actual_bond, + ); + + correct_reporters.push(dispute.by.clone()); + } else { + let (imbalance, _) = CurrencyOf::::slash_reserved_named( + &Self::reserve_id(), + &dispute.by, + actual_bond.saturated_into::().saturated_into(), + ); + overall_imbalance.subsume(imbalance); + } + } + + // Fold all the imbalances into one and reward the correct reporters. The + // number of correct reporters might be zero if the market defaults to the + // report after abandoned dispute. In that case, the rewards remain slashed. + if let Some(reward_per_each) = + overall_imbalance.peek().checked_div(&correct_reporters.len().saturated_into()) + { + for correct_reporter in &correct_reporters { + let (actual_reward, leftover) = overall_imbalance.split(reward_per_each); + overall_imbalance = leftover; + CurrencyOf::::resolve_creating(correct_reporter, actual_reward); + } } + + T::Slash::on_unbalanced(overall_imbalance); + + Ok(Some(resolved_outcome)) } fn get_auto_resolve( - disputes: &[MarketDispute], - _: &Self::MarketId, + market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - Ok(Self::get_auto_resolve(disputes, market)) + let disputes = Disputes::::get(market_id); + Ok(Self::get_auto_resolve(disputes.as_slice(), market)) } - fn has_failed( - _: &[MarketDispute], - _: &Self::MarketId, - market: &MarketOf, - ) -> Result { + fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism From c8699d4d5237d21a63672d89a029da74dbe8c5d5 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 11 Jan 2023 16:45:53 +0100 Subject: [PATCH 017/368] wip --- Cargo.lock | 1 + primitives/src/traits/dispute_api.rs | 7 +++ zrml/authorized/src/lib.rs | 31 +++++------- zrml/court/src/lib.rs | 25 +++++----- zrml/prediction-markets/src/lib.rs | 72 +++++++++++----------------- zrml/simple-disputes/Cargo.toml | 4 ++ zrml/simple-disputes/src/lib.rs | 65 ++++++++++++++++++++++--- zrml/simple-disputes/src/weights.rs | 67 ++++++++++++++++++++++++++ 8 files changed, 191 insertions(+), 81 deletions(-) create mode 100644 zrml/simple-disputes/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index c19c8d2e9..19cff4536 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13004,6 +13004,7 @@ dependencies = [ "sp-io", "sp-runtime", "zeitgeist-primitives", + "zrml-global-disputes", "zrml-market-commons", ] diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index a332874c9..5a87f8e00 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -74,6 +74,13 @@ pub trait DisputeApi { market_id: &Self::MarketId, market: &MarketOfDisputeApi, ) -> Result; + + /// Called, when a global dispute is started. + /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. + fn on_global_dispute( + market_id: &Self::MarketId, + market: &MarketOfDisputeApi, + ) -> DispatchResult; } type MarketOfDisputeResolutionApi = Market< diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index d33cc2789..7c46b2ae4 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -46,10 +46,7 @@ mod pallet { use sp_runtime::{traits::Saturating, DispatchError}; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, - types::{ - AuthorityReport, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, - OutcomeReport, - }, + types::{AuthorityReport, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport}, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -156,8 +153,6 @@ mod pallet { MarketDoesNotHaveDisputeMechanismAuthorized, /// An account attempts to submit a report to an undisputed market. MarketIsNotDisputed, - /// Only one dispute is allowed. - OnlyOneDisputeAllowed, /// The report does not match the market's type. OutcomeMismatch, } @@ -195,21 +190,15 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute( - disputes: &[MarketDispute], - _: &Self::MarketId, - market: &MarketOf, - ) -> DispatchResult { + fn on_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); - ensure!(disputes.is_empty(), Error::::OnlyOneDisputeAllowed); Ok(()) } fn on_resolution( - _: &[MarketDispute], market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -222,7 +211,6 @@ mod pallet { } fn get_auto_resolve( - _: &[MarketDispute], market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -233,11 +221,7 @@ mod pallet { Ok(Self::get_auto_resolve(market_id)) } - fn has_failed( - _: &[MarketDispute], - _: &Self::MarketId, - market: &MarketOf, - ) -> Result { + fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized @@ -245,6 +229,15 @@ mod pallet { Ok(false) } + + fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Authorized, + Error::::MarketDoesNotHaveDisputeMechanismAuthorized + ); + + Ok(()) + } } impl AuthorizedPalletApi for Pallet where T: Config {} diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index c1d08183b..e48020cd3 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -509,17 +509,14 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute( - disputes: &[MarketDispute], - market_id: &Self::MarketId, - market: &MarketOf, - ) -> DispatchResult { + fn on_dispute(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); let jurors: Vec<_> = Jurors::::iter().collect(); - let necessary_jurors_num = Self::necessary_jurors_num(disputes); + // TODO &[] was disputes list before: how to handle it now without disputes from pm? + let necessary_jurors_num = Self::necessary_jurors_num(&[]); let mut rng = Self::rng(); let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); let curr_block_num = >::block_number(); @@ -535,7 +532,6 @@ mod pallet { // voted outcome (winner of the losing majority) are placed as tardy instead of // being slashed. fn on_resolution( - _: &[MarketDispute], market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -564,7 +560,6 @@ mod pallet { } fn get_auto_resolve( - _: &[MarketDispute], _: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -575,17 +570,21 @@ mod pallet { Ok(None) } - fn has_failed( - _: &[MarketDispute], - _: &Self::MarketId, - market: &MarketOf, - ) -> Result { + fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); Ok(false) } + + fn on_global_dispute(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + Error::::MarketDoesNotHaveCourtMechanism + ); + Ok(()) + } } impl CourtPalletApi for Pallet where T: Config {} diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 7b009462b..3bb07cff3 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1297,36 +1297,33 @@ mod pallet { Error::::InvalidDisputeMechanism ); - let disputes = >::get(market_id); - ensure!( - disputes.len() == T::MaxDisputes::get() as usize, - Error::::MaxDisputesNeeded - ); - ensure!( T::GlobalDisputes::is_not_started(&market_id), Error::::GlobalDisputeAlreadyStarted ); - // add report outcome to voting choices - if let Some(report) = &market.report { - T::GlobalDisputes::push_voting_outcome( - &market_id, - report.outcome.clone(), - &report.by, - >::zero(), - )?; - } + let has_failed = match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + T::Authorized::has_failed(market_id, &market)? + } + MarketDisputeMechanism::Court => T::Court::has_failed(market_id, &market)?, + MarketDisputeMechanism::SimpleDisputes => { + T::SimpleDisputes::has_failed(market_id, &market)? + } + }; + ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); - for (index, MarketDispute { at: _, by, outcome }) in disputes.iter().enumerate() { - let dispute_bond = default_dispute_bond::(index); - T::GlobalDisputes::push_voting_outcome( - &market_id, - outcome.clone(), - by, - dispute_bond, - )?; - } + match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + T::Authorized::on_global_dispute(market_id, &market)? + } + MarketDisputeMechanism::Court => { + T::Court::on_global_dispute(market_id, &market)? + } + MarketDisputeMechanism::SimpleDisputes => { + T::SimpleDisputes::on_global_dispute(market_id, &market)? + } + }; // TODO(#372): Allow court with global disputes. // ensure, that global disputes controls the resolution now @@ -1573,8 +1570,8 @@ mod pallet { MarketStartTooLate, /// The maximum number of disputes has been reached. MaxDisputesReached, - /// The maximum number of disputes is needed for this operation. - MaxDisputesNeeded, + /// The market dispute mechanism has not failed. + MarketDisputeMechanismNotFailed, /// Tried to settle missing bond. MissingBond, /// The number of categories for a categorical market is too low. @@ -1980,7 +1977,7 @@ mod pallet { /// Clears this market from being stored for automatic resolution. fn clear_auto_resolve(market_id: &MarketIdOf) -> Result<(u32, u32), DispatchError> { let market = >::market(market_id)?; - let (ids_len, disputes_len) = match market.status { + let ids_len = match market.status { MarketStatus::Reported => { let report = market.report.ok_or(Error::::MarketIsNotReported)?; let dispute_duration_ends_at_block = @@ -1995,7 +1992,6 @@ mod pallet { ) } MarketStatus::Disputed => { - let disputes = Disputes::::get(market_id); // TODO(#782): use multiple benchmarks paths for different dispute mechanisms let auto_resolve_block_opt = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { @@ -2010,15 +2006,15 @@ mod pallet { }; if let Some(auto_resolve_block) = auto_resolve_block_opt { let ids_len = remove_auto_resolve::(market_id, auto_resolve_block); - (ids_len, disputes.len() as u32) + (ids_len, 0u32) } else { - (0u32, disputes.len() as u32) + (0u32, 0u32) } } _ => (0u32, 0u32), }; - Ok((ids_len, disputes_len)) + Ok((ids_len, 1u32)) } pub(crate) fn do_buy_complete_set( @@ -2319,7 +2315,7 @@ mod pallet { } else { let imbalance = Self::slash_oracle_bond(market_id, None)?; // TODO what should be done with the OracleBond imbalance? - overall_imbalance.subsume(imbalance); + // overall_imbalance.subsume(imbalance); } Ok(resolved_outcome) @@ -2353,7 +2349,7 @@ mod pallet { m.resolved_outcome = Some(resolved_outcome.clone()); Ok(()) })?; - Disputes::::remove(market_id); + Self::deposit_event(Event::MarketResolved( *market_id, MarketStatus::Resolved, @@ -2727,16 +2723,6 @@ mod pallet { } } - // No-one can bound more than BalanceOf, therefore, this functions saturates - pub(crate) fn default_dispute_bond(n: usize) -> BalanceOf - where - T: Config, - { - T::DisputeBond::get().saturating_add( - T::DisputeFactor::get().saturating_mul(n.saturated_into::().into()), - ) - } - fn remove_item(items: &mut BoundedVec, item: &I) { if let Some(pos) = items.iter().position(|i| i == item) { items.swap_remove(pos); diff --git a/zrml/simple-disputes/Cargo.toml b/zrml/simple-disputes/Cargo.toml index 11629771a..85f45c0f1 100644 --- a/zrml/simple-disputes/Cargo.toml +++ b/zrml/simple-disputes/Cargo.toml @@ -8,6 +8,7 @@ sp-runtime = { branch = "polkadot-v0.9.26", default-features = false, git = "htt zeitgeist-primitives = { default-features = false, path = "../../primitives" } zrml-market-commons = { default-features = false, path = "../market-commons" } orml-traits = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library" } +zrml-global-disputes = { default-features = false, path = "../global-disputes", optional = true } [dev-dependencies] pallet-balances = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } @@ -34,6 +35,9 @@ std = [ try-runtime = [ "frame-support/try-runtime", ] +with-global-disputes = [ + "zrml-global-disputes", +] [package] authors = ["Zeitgeist PM "] diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 0b19c7c46..5405792ab 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -23,20 +23,21 @@ extern crate alloc; mod mock; mod simple_disputes_pallet_api; mod tests; +pub mod weights; pub use pallet::*; pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; #[frame_support::pallet] mod pallet { - use crate::SimpleDisputesPalletApi; + use crate::{weights::WeightInfoZeitgeist, SimpleDisputesPalletApi}; use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, ensure, - pallet_prelude::*, + pallet_prelude::{Blake2_128Concat, DispatchResultWithPostInfo, StorageMap, ValueQuery}, traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency, OnUnbalanced}, - transactional, PalletId, + transactional, BoundedVec, PalletId, }; use frame_system::pallet_prelude::*; use orml_traits::currency::NamedMultiReservableCurrency; @@ -51,6 +52,8 @@ mod pallet { Report, }, }; + #[cfg(feature = "with-global-disputes")] + use zrml_global_disputes::GlobalDisputesPalletApi; use zrml_market_commons::MarketCommonsPalletApi; #[pallet::config] @@ -82,6 +85,14 @@ mod pallet { #[pallet::constant] type DisputeFactor: Get>; + /// See [`GlobalDisputesPalletApi`]. + #[cfg(feature = "with-global-disputes")] + type GlobalDisputes: GlobalDisputesPalletApi< + MarketIdOf, + Self::AccountId, + BalanceOf, + >; + /// The identifier of individual markets. type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, @@ -101,6 +112,9 @@ mod pallet { /// Handler for slashed funds. type Slash: OnUnbalanced>; + + /// Weights generated by benchmarks + type WeightInfo: WeightInfoZeitgeist; } type BalanceOf = @@ -340,6 +354,8 @@ mod pallet { T::Slash::on_unbalanced(overall_imbalance); + Disputes::::remove(market_id); + Ok(Some(resolved_outcome)) } @@ -355,13 +371,50 @@ mod pallet { Ok(Self::get_auto_resolve(disputes.as_slice(), market)) } - fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { + fn has_failed( + market_id: &Self::MarketId, + market: &MarketOf, + ) -> Result { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - // TODO when does simple disputes fail? - Ok(false) + let disputes = >::get(market_id); + Ok(disputes.len() == T::MaxDisputes::get() as usize) + } + + fn on_global_dispute( + #[allow(dead_code, unused)] market_id: &Self::MarketId, + market: &MarketOf, + ) -> DispatchResult { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, + Error::::MarketDoesNotHaveSimpleDisputesMechanism + ); + #[cfg(feature = "with-global-disputes")] + { + let disputes = >::get(market_id); + // add report outcome to voting choices + if let Some(report) = &market.report { + T::GlobalDisputes::push_voting_outcome( + market_id, + report.outcome.clone(), + &report.by, + >::zero(), + )?; + } + + for (index, MarketDispute { at: _, by, outcome }) in disputes.iter().enumerate() { + let dispute_bond = default_dispute_bond::(index); + T::GlobalDisputes::push_voting_outcome( + market_id, + outcome.clone(), + by, + dispute_bond, + )?; + } + } + Ok(()) } } diff --git a/zrml/simple-disputes/src/weights.rs b/zrml/simple-disputes/src/weights.rs new file mode 100644 index 000000000..248c8f7e0 --- /dev/null +++ b/zrml/simple-disputes/src/weights.rs @@ -0,0 +1,67 @@ +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +//! Autogenerated weights for zrml_prediction_markets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-09, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/zeitgeist +// benchmark +// pallet +// --chain=dev +// --steps=10 +// --repeat=1000 +// --pallet=zrml_prediction_markets +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./zrml/prediction-markets/src/weights.rs +// --template=./misc/weight_template.hbs + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use core::marker::PhantomData; +use frame_support::{traits::Get, weights::Weight}; + +/// Trait containing the required functions for weight retrival within +/// zrml_prediction_markets (automatically generated) +pub trait WeightInfoZeitgeist { + fn start_global_dispute(m: u32, n: u32) -> Weight; +} + +pub struct WeightInfo(PhantomData); +impl WeightInfoZeitgeist for WeightInfo { + // Storage: MarketCommons Markets (r:1 w:0) + // Storage: PredictionMarkets Disputes (r:1 w:0) + // Storage: GlobalDisputes Winners (r:1 w:1) + // Storage: GlobalDisputes Outcomes (r:7 w:7) + // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) + fn start_global_dispute(m: u32, n: u32) -> Weight { + (94_106_000 as Weight) + // Standard Error: 0 + .saturating_add((15_000 as Weight).saturating_mul(m as Weight)) + // Standard Error: 0 + .saturating_add((20_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(12 as Weight)) + .saturating_add(T::DbWeight::get().writes(10 as Weight)) + } +} From ae91749216a69d96b510b3c7473e4b880870dd19 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 12 Jan 2023 16:06:55 +0100 Subject: [PATCH 018/368] wip --- Cargo.lock | 3 ++ primitives/src/traits/dispute_api.rs | 9 ++--- zrml/authorized/src/lib.rs | 10 +++++ zrml/authorized/src/mock.rs | 11 ------ zrml/court/src/lib.rs | 10 +++++ zrml/court/src/mock.rs | 6 --- zrml/prediction-markets/src/lib.rs | 34 +++++++++++----- zrml/prediction-markets/src/mock.rs | 9 ++++- zrml/simple-disputes/Cargo.toml | 3 ++ zrml/simple-disputes/src/lib.rs | 9 +++++ zrml/simple-disputes/src/mock.rs | 59 ++++++++++++++++++++++++---- zrml/simple-disputes/src/tests.rs | 15 +++---- 12 files changed, 131 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19cff4536..2432a6a68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12996,9 +12996,12 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "orml-currencies", + "orml-tokens", "orml-traits", "pallet-balances", "pallet-timestamp", + "pallet-treasury", "parity-scale-codec", "scale-info", "sp-io", diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 5a87f8e00..53f40585b 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -81,6 +81,10 @@ pub trait DisputeApi { market_id: &Self::MarketId, market: &MarketOfDisputeApi, ) -> DispatchResult; + + /// Called, when a market is destroyed. + /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. + fn clear(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; } type MarketOfDisputeResolutionApi = Market< @@ -134,9 +138,4 @@ pub trait DisputeResolutionApi { /// /// 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, Self::MaxDisputes>; } diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 7c46b2ae4..ea4fd70ae 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -238,6 +238,16 @@ mod pallet { Ok(()) } + + fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Authorized, + Error::::MarketDoesNotHaveDisputeMechanismAuthorized + ); + + AuthorizedOutcomeReports::::remove(market_id); + Ok(()) + } } impl AuthorizedPalletApi for Pallet where T: Config {} diff --git a/zrml/authorized/src/mock.rs b/zrml/authorized/src/mock.rs index 056e59f1e..49b277a68 100644 --- a/zrml/authorized/src/mock.rs +++ b/zrml/authorized/src/mock.rs @@ -112,17 +112,6 @@ impl DisputeResolutionApi for MockResolution { ids.len() as u32 }) } - - fn get_disputes( - _market_id: &Self::MarketId, - ) -> BoundedVec, Self::MaxDisputes> { - BoundedVec::try_from(vec![MarketDispute { - at: 42u64, - by: BOB, - outcome: OutcomeReport::Scalar(42), - }]) - .unwrap() - } } impl crate::Config for Runtime { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index e48020cd3..2368bfeac 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -585,6 +585,16 @@ mod pallet { ); Ok(()) } + + fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + Error::::MarketDoesNotHaveCourtMechanism + ); + let _ = Votes::::clear_prefix(market_id, u32::max_value(), None); + let _ = RequestedJurors::::clear_prefix(market_id, u32::max_value(), None); + Ok(()) + } } impl CourtPalletApi for Pallet where T: Config {} diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 11cfe03ee..174eeebbc 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -98,12 +98,6 @@ impl DisputeResolutionApi for NoopResolution { fn remove_auto_resolve(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> u32 { 0u32 } - - fn get_disputes( - _market_id: &Self::MarketId, - ) -> BoundedVec, Self::MaxDisputes> { - Default::default() - } } impl crate::Config for Runtime { diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 3bb07cff3..967214d5b 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -261,8 +261,8 @@ mod pallet { let open_ids_len = Self::clear_auto_open(&market_id)?; let close_ids_len = Self::clear_auto_close(&market_id)?; let (ids_len, disputes_len) = Self::clear_auto_resolve(&market_id)?; + Self::clear_dispute_mechanism(&market_id)?; >::remove_market(&market_id)?; - Disputes::::remove(market_id); Self::deposit_event(Event::MarketDestroyed(market_id)); @@ -1787,6 +1787,17 @@ mod pallet { #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); + /// For each market, this holds the dispute information for each dispute that's + /// been issued. + #[pallet::storage] + pub type Disputes = StorageMap< + _, + Blake2_128Concat, + MarketIdOf, + BoundedVec, T::MaxDisputes>, + ValueQuery, + >; + #[pallet::storage] pub type MarketIdsPerOpenBlock = StorageMap< _, @@ -2014,7 +2025,19 @@ mod pallet { _ => (0u32, 0u32), }; - Ok((ids_len, 1u32)) + Ok((ids_len, 0u32)) + } + + fn clear_dispute_mechanism(market_id: &MarketIdOf) -> DispatchResult { + let market = >::market(market_id)?; + match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => T::Authorized::clear(market_id, &market)?, + MarketDisputeMechanism::Court => T::Court::clear(market_id, &market)?, + MarketDisputeMechanism::SimpleDisputes => { + T::SimpleDisputes::clear(market_id, &market)? + } + }; + Ok(()) } pub(crate) fn do_buy_complete_set( @@ -2779,12 +2802,5 @@ mod pallet { fn remove_auto_resolve(market_id: &Self::MarketId, resolve_at: Self::BlockNumber) -> u32 { remove_auto_resolve::(market_id, resolve_at) } - - fn get_disputes( - market_id: &Self::MarketId, - ) -> BoundedVec, Self::MaxDisputes> - { - Disputes::::get(market_id) - } } } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index f44b7b8ba..a746f9cb1 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -142,8 +142,6 @@ impl crate::Config for Runtime { type CloseOrigin = EnsureSignedBy; type Court = Court; type DestroyOrigin = EnsureSignedBy; - type DisputeBond = DisputeBond; - type DisputeFactor = DisputeFactor; type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; @@ -305,10 +303,17 @@ impl zrml_rikiddo::Config for Runtime { } impl zrml_simple_disputes::Config for Runtime { + type AssetManager = AssetManager; type Event = Event; + type DisputeBond = DisputeBond; + type DisputeFactor = DisputeFactor; type DisputeResolution = prediction_markets::Pallet; type MarketCommons = MarketCommons; + type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; + type PredictionMarketsPalletId = PmPalletId; + type Slash = Treasury; + type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } #[cfg(feature = "with-global-disputes")] diff --git a/zrml/simple-disputes/Cargo.toml b/zrml/simple-disputes/Cargo.toml index 85f45c0f1..59b787812 100644 --- a/zrml/simple-disputes/Cargo.toml +++ b/zrml/simple-disputes/Cargo.toml @@ -11,7 +11,10 @@ orml-traits = { branch = "polkadot-v0.9.26", default-features = false, git = "ht zrml-global-disputes = { default-features = false, path = "../global-disputes", optional = true } [dev-dependencies] +orml-currencies = { branch = "polkadot-v0.9.26", git = "https://github.com/open-web3-stack/open-runtime-module-library"} +orml-tokens = { branch = "polkadot-v0.9.26", git = "https://github.com/open-web3-stack/open-runtime-module-library"} pallet-balances = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } +pallet-treasury = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } pallet-timestamp = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } sp-io = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } zeitgeist-primitives = { default-features = false, features = ["mock"], path = "../../primitives" } diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 5405792ab..095fa3ad7 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -416,6 +416,15 @@ mod pallet { } Ok(()) } + + fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, + Error::::MarketDoesNotHaveSimpleDisputesMechanism + ); + Disputes::::remove(market_id); + Ok(()) + } } impl SimpleDisputesPalletApi for Pallet where T: Config {} diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 5e6afbcd6..4efacea60 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -30,7 +30,7 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, MaxReserves, MinimumPeriod, PmPalletId, SimpleDisputesPalletId, + BlockHashCount, MaxReserves, MinimumPeriod, PmPalletId, SimpleDisputesPalletId, DisputeFactor, DisputeBond, MaxDisputes, }, traits::DisputeResolutionApi, types::{ @@ -47,8 +47,10 @@ construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsicTest, { Balances: pallet_balances::{Call, Config, Event, Pallet, Storage}, + AssetManager: orml_currencies::{Call, Pallet, Storage}, MarketCommons: zrml_market_commons::{Pallet, Storage}, SimpleDisputes: zrml_simple_disputes::{Event, Pallet, Storage}, + Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, System: frame_system::{Call, Config, Event, Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet}, } @@ -86,19 +88,20 @@ impl DisputeResolutionApi for NoopResolution { fn remove_auto_resolve(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> u32 { 0u32 } - - fn get_disputes( - _market_id: &Self::MarketId, - ) -> BoundedVec, Self::MaxDisputes> { - Default::default() - } } impl crate::Config for Runtime { + type AssetManager = AssetManager; type Event = (); + type DisputeBond = DisputeBond; + type DisputeFactor = DisputeFactor; type DisputeResolution = NoopResolution; type MarketCommons = MarketCommons; + type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; + type PredictionMarketsPalletId = PmPalletId; + type Slash = Treasury; + type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } impl frame_system::Config for Runtime { @@ -140,6 +143,29 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); } +impl orml_currencies::Config for Runtime { + type GetNativeCurrencyId = GetNativeCurrencyId; + type MultiCurrency = Tokens; + type NativeCurrency = BasicCurrencyAdapter; + type WeightInfo = (); +} + +impl orml_tokens::Config for Runtime { + type Amount = Amount; + type Balance = Balance; + type CurrencyId = CurrencyId; + type DustRemovalWhitelist = Everything; + type Event = Event; + type ExistentialDeposits = ExistentialDeposits; + type MaxLocks = (); + type MaxReserves = MaxReserves; + type OnDust = (); + type OnKilledTokenAccount = (); + type OnNewTokenAccount = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + impl zrml_market_commons::Config for Runtime { type Currency = Balances; type MarketId = MarketId; @@ -154,6 +180,25 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } +impl pallet_treasury::Config for Runtime { + type ApproveOrigin = EnsureSignedBy; + type Burn = (); + type BurnDestination = (); + type Currency = Balances; + type Event = Event; + type MaxApprovals = MaxApprovals; + type OnSlash = (); + type PalletId = TreasuryPalletId; + type ProposalBond = (); + type ProposalBondMinimum = (); + type ProposalBondMaximum = (); + type RejectOrigin = EnsureSignedBy; + type SpendFunds = (); + type SpendOrigin = NeverEnsureOrigin; + type SpendPeriod = (); + type WeightInfo = (); +} + pub struct ExtBuilder; impl ExtBuilder { diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index 251377533..5b0233045 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -19,9 +19,9 @@ use crate::{ mock::{ExtBuilder, Runtime, SimpleDisputes}, - Error, MarketOf, + Error, MarketOf, Disputes, }; -use frame_support::assert_noop; +use frame_support::{assert_noop, BoundedVec}; use zeitgeist_primitives::{ traits::DisputeApi, types::{ @@ -53,7 +53,7 @@ fn on_dispute_denies_non_simple_disputes_markets() { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::Court; assert_noop!( - SimpleDisputes::on_dispute(&[], &0, &market), + SimpleDisputes::on_dispute(&0, &market), Error::::MarketDoesNotHaveSimpleDisputesMechanism ); }); @@ -65,7 +65,7 @@ fn on_resolution_denies_non_simple_disputes_markets() { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::Court; assert_noop!( - SimpleDisputes::on_resolution(&[], &0, &market), + SimpleDisputes::on_resolution(&0, &market), Error::::MarketDoesNotHaveSimpleDisputesMechanism ); }); @@ -76,12 +76,13 @@ fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outc ExtBuilder.build().execute_with(|| { let mut market = DEFAULT_MARKET; market.status = MarketStatus::Disputed; - let disputes = [ + let disputes = BoundedVec::try_from([ MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(0) }, MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(20) }, - ]; + ].to_vec()).unwrap(); + Disputes::::insert(&0, &disputes); assert_eq!( - &SimpleDisputes::on_resolution(&disputes, &0, &market).unwrap().unwrap(), + &SimpleDisputes::on_resolution(&0, &market).unwrap().unwrap(), &disputes.last().unwrap().outcome ) }); From 228d3338f69539f958d73a140eecdfd49ed4539f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 13 Jan 2023 11:09:09 +0100 Subject: [PATCH 019/368] some mock preparation --- primitives/src/traits/dispute_api.rs | 4 +- zrml/authorized/src/mock.rs | 5 +- zrml/court/src/lib.rs | 2 +- zrml/prediction-markets/src/benchmarks.rs | 74 ++++++++--------------- zrml/prediction-markets/src/lib.rs | 18 +++--- zrml/prediction-markets/src/mock.rs | 2 + zrml/simple-disputes/src/lib.rs | 5 +- zrml/simple-disputes/src/mock.rs | 61 +++++++++++++++++-- zrml/simple-disputes/src/tests.rs | 14 +++-- 9 files changed, 111 insertions(+), 74 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 53f40585b..d415be8d3 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -15,8 +15,8 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -use crate::{market::MarketDispute, outcome_report::OutcomeReport, types::Market}; -use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight, BoundedVec}; +use crate::{outcome_report::OutcomeReport, types::Market}; +use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight}; use sp_runtime::DispatchError; // Abstraction of the market type, which is not a part of `DisputeApi` because Rust doesn't support diff --git a/zrml/authorized/src/mock.rs b/zrml/authorized/src/mock.rs index 49b277a68..2d7b3014a 100644 --- a/zrml/authorized/src/mock.rs +++ b/zrml/authorized/src/mock.rs @@ -25,7 +25,6 @@ use frame_support::{ construct_runtime, ord_parameter_types, pallet_prelude::{DispatchError, Weight}, traits::Everything, - BoundedVec, }; use frame_system::EnsureSignedBy; use sp_runtime::{ @@ -39,8 +38,8 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketDispute, - MarketId, Moment, OutcomeReport, UncheckedExtrinsicTest, + AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, + MarketId, Moment, UncheckedExtrinsicTest, }, }; diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 2368bfeac..22f8180c1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -578,7 +578,7 @@ mod pallet { Ok(false) } - fn on_global_dispute(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index efac65a47..bd78f52fb 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -222,7 +222,7 @@ fn setup_reported_categorical_market_with_pool::MarketCommons as MarketCommonsPalletApi>::MarketId: From<::MarketId>, } @@ -246,13 +246,8 @@ benchmarks! { let pool_id = >::market_pool(&market_id)?; - for i in 1..=d { - let outcome = OutcomeReport::Categorical((i % a).saturated_into()); - let disputor = account("disputor", i, 0); - let dispute_bond = crate::pallet::default_dispute_bond::(i as usize); - T::AssetManager::deposit(Asset::Ztg, &disputor, dispute_bond)?; - let _ = Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id, outcome)?; - } + let disputor = account("disputor", 1, 0); + let _ = Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id)?; let market = >::market(&market_id)?; @@ -467,24 +462,16 @@ benchmarks! { let outcome = OutcomeReport::Scalar(0); let disputor = account("disputor", 1, 0); - let dispute_bond = crate::pallet::default_dispute_bond::(0_usize); - T::AssetManager::deposit( - Asset::Ztg, - &disputor, - dispute_bond, - )?; - Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id, outcome)?; - let disputes = Disputes::::get(market_id); - // Authorize the outcome with the highest number of correct reporters to maximize the - // number of transfers required (0 has (d+1)//2 reports, 1 has d//2 reports). + Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id)?; + + let now = >::block_number(); AuthorizedPallet::::authorize_market_outcome( T::AuthorizedDisputeResolutionOrigin::successful_origin(), market_id.into(), OutcomeReport::Scalar(0), )?; - let last_dispute = disputes.last().unwrap(); - let resolves_at = last_dispute.at.saturating_add(market.deadlines.dispute_duration); + let resolves_at = now.saturating_add(::CorrectionPeriod::get()); for i in 0..r { MarketIdsPerDisputeBlock::::try_mutate( resolves_at, @@ -519,17 +506,9 @@ benchmarks! { Ok(()) })?; - let outcome = OutcomeReport::Categorical(0u16); let disputor = account("disputor", 1, 0); - let dispute_bond = crate::pallet::default_dispute_bond::(0_usize); - T::AssetManager::deposit( - Asset::Ztg, - &disputor, - dispute_bond, - )?; - Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id, outcome)?; + Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id)?; - let disputes = Disputes::::get(market_id); // Authorize the outcome with the highest number of correct reporters to maximize the // number of transfers required (0 has (d+1)//2 reports, 1 has d//2 reports). AuthorizedPallet::::authorize_market_outcome( @@ -538,9 +517,9 @@ benchmarks! { OutcomeReport::Categorical(0), )?; - let last_dispute = disputes.last().unwrap(); let market = >::market(&market_id)?; - let resolves_at = last_dispute.at.saturating_add(market.deadlines.dispute_duration); + let now = >::block_number(); + let resolves_at = now.saturating_add(::CorrectionPeriod::get()); for i in 0..r { MarketIdsPerDisputeBlock::::try_mutate( resolves_at, @@ -798,22 +777,27 @@ benchmarks! { market_ids_1.try_push(i.saturated_into()).unwrap(); } - let max_dispute_len = T::MaxDisputes::get(); + let disputor: T::AccountId = account("Disputor", 1, 0); + let _ = Call::::dispute { + market_id, + } + .dispatch_bypass_filter(RawOrigin::Signed(disputor.clone()).into())?; + + let max_dispute_len = ::MaxDisputes::get(); for i in 0..max_dispute_len { // ensure that the MarketIdsPerDisputeBlock does not interfere // with the start_global_dispute execution block >::set_block_number(i.saturated_into()); - let disputor: T::AccountId = account("Disputor", i, 0); - T::AssetManager::deposit(Asset::Ztg, &disputor, (u128::MAX).saturated_into())?; - let _ = Call::::dispute { + let reserver: T::AccountId = account("Reserver", i, 0); + T::AssetManager::deposit(Asset::Ztg, &reserver, (u128::MAX).saturated_into())?; + let _ = zrml_simple_disputes::Call::::reserve_outcome { market_id, - outcome: OutcomeReport::Scalar(i.into()), - } - .dispatch_bypass_filter(RawOrigin::Signed(disputor.clone()).into())?; + outcome: OutcomeReport::Scalar(i.saturated_into()), + }.dispatch_bypass_filter(RawOrigin::Signed(reserver.clone()).into())?; } let market = >::market(&market_id.saturated_into()).unwrap(); - let disputes = Disputes::::get(market_id); + let disputes = zrml_simple_disputes::Disputes::::get(market_id); let last_dispute = disputes.last().unwrap(); let dispute_duration_ends_at_block = last_dispute.at + market.deadlines.dispute_duration; let mut market_ids_2: BoundedVec, CacheSize> = BoundedVec::try_from( @@ -858,9 +842,7 @@ benchmarks! { let market = >::market(&market_id)?; - // only one dispute allowed for authorized mdm - let dispute_outcome = OutcomeReport::Scalar(1u128); - let call = Call::::dispute { market_id, outcome: dispute_outcome }; + let call = Call::::dispute { market_id }; }: { call.dispatch_bypass_filter(RawOrigin::Signed(caller).into())?; } @@ -908,10 +890,8 @@ benchmarks! { Pallet::::dispute( RawOrigin::Signed(caller).into(), market_id, - OutcomeReport::Categorical(0), )?; - // Authorize the outcome with the highest number of correct reporters to maximize the - // number of transfers required (0 has (d+1)//2 reports, 1 has d//2 reports). + AuthorizedPallet::::authorize_market_outcome( T::AuthorizedDisputeResolutionOrigin::successful_origin(), market_id.into(), @@ -953,10 +933,8 @@ benchmarks! { Pallet::::dispute( RawOrigin::Signed(caller).into(), market_id, - OutcomeReport::Scalar(1) )?; - // Authorize the outcome with the highest number of correct reporters to maximize the - // number of transfers required (0 has (d+1)//2 reports, 1 has d//2 reports). + AuthorizedPallet::::authorize_market_outcome( T::AuthorizedDisputeResolutionOrigin::successful_origin(), market_id.into(), diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 967214d5b..141c32000 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -51,7 +51,7 @@ mod pallet { use orml_traits::{MultiCurrency, NamedMultiReservableCurrency}; use sp_arithmetic::per_things::{Perbill, Percent}; use sp_runtime::{ - traits::{CheckedDiv, Saturating, Zero}, + traits::{Saturating, Zero}, DispatchError, DispatchResult, SaturatedConversion, }; use zeitgeist_primitives::{ @@ -1304,24 +1304,24 @@ mod pallet { let has_failed = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::has_failed(market_id, &market)? + T::Authorized::has_failed(&market_id, &market)? } - MarketDisputeMechanism::Court => T::Court::has_failed(market_id, &market)?, + MarketDisputeMechanism::Court => T::Court::has_failed(&market_id, &market)?, MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::has_failed(market_id, &market)? + T::SimpleDisputes::has_failed(&market_id, &market)? } }; ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::on_global_dispute(market_id, &market)? + T::Authorized::on_global_dispute(&market_id, &market)? } MarketDisputeMechanism::Court => { - T::Court::on_global_dispute(market_id, &market)? + T::Court::on_global_dispute(&market_id, &market)? } MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_global_dispute(market_id, &market)? + T::SimpleDisputes::on_global_dispute(&market_id, &market)? } }; @@ -1988,7 +1988,7 @@ mod pallet { /// Clears this market from being stored for automatic resolution. fn clear_auto_resolve(market_id: &MarketIdOf) -> Result<(u32, u32), DispatchError> { let market = >::market(market_id)?; - let ids_len = match market.status { + let (ids_len, mdm_len) = match market.status { MarketStatus::Reported => { let report = market.report.ok_or(Error::::MarketIsNotReported)?; let dispute_duration_ends_at_block = @@ -2025,7 +2025,7 @@ mod pallet { _ => (0u32, 0u32), }; - Ok((ids_len, 0u32)) + Ok((ids_len, mdm_len)) } fn clear_dispute_mechanism(market_id: &MarketIdOf) -> DispatchResult { diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index a746f9cb1..988ca53b4 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -308,6 +308,8 @@ impl zrml_simple_disputes::Config for Runtime { type DisputeBond = DisputeBond; type DisputeFactor = DisputeFactor; type DisputeResolution = prediction_markets::Pallet; + #[cfg(feature = "with-global-disputes")] + type GlobalDisputes = GlobalDisputes; type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index f0fd01684..040ef8dda 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -39,10 +39,11 @@ mod pallet { traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency, OnUnbalanced}, transactional, BoundedVec, PalletId, }; + use alloc::vec::Vec; use frame_system::pallet_prelude::*; use orml_traits::currency::NamedMultiReservableCurrency; use sp_runtime::{ - traits::{CheckedDiv, Saturating}, + traits::{CheckedDiv, Saturating, Zero}, DispatchError, SaturatedConversion, }; use zeitgeist_primitives::{ @@ -196,7 +197,7 @@ mod pallet { let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; let now = >::block_number(); - let disputes = Disputes::::get(&market_id); + let disputes = Disputes::::get(market_id); let num_disputes: u32 = disputes.len().saturated_into(); Self::ensure_can_not_dispute_the_same_outcome(&disputes, report, &outcome)?; diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 4efacea60..16a23d2a7 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -22,23 +22,38 @@ use frame_support::{ construct_runtime, pallet_prelude::{DispatchError, Weight}, traits::Everything, - BoundedVec, }; +use frame_system::EnsureSignedBy; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; +use frame_support::ord_parameter_types; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, MaxReserves, MinimumPeriod, PmPalletId, SimpleDisputesPalletId, DisputeFactor, DisputeBond, MaxDisputes, + BlockHashCount, DisputeBond, DisputeFactor, MaxDisputes, MaxReserves, MinimumPeriod, + PmPalletId, SimpleDisputesPalletId, GetNativeCurrencyId, MaxApprovals, TreasuryPalletId, }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketDispute, - MarketId, Moment, UncheckedExtrinsicTest, + AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, + MarketId, Moment, UncheckedExtrinsicTest, BasicCurrencyAdapter, }, }; +pub const ALICE: AccountIdTest = 0; +pub const BOB: AccountIdTest = 1; +pub const CHARLIE: AccountIdTest = 2; +pub const DAVE: AccountIdTest = 3; +pub const EVE: AccountIdTest = 4; +pub const FRED: AccountIdTest = 5; +pub const SUDO: AccountIdTest = 69; + +ord_parameter_types! { + pub const Sudo: AccountIdTest = SUDO; +} + +#[cfg(feature = "with-global-disputes")] construct_runtime!( pub enum Runtime where @@ -50,9 +65,30 @@ construct_runtime!( AssetManager: orml_currencies::{Call, Pallet, Storage}, MarketCommons: zrml_market_commons::{Pallet, Storage}, SimpleDisputes: zrml_simple_disputes::{Event, Pallet, Storage}, + GlobalDisputes: zrml_global_disputes::{Event, Pallet, Storage}, + System: frame_system::{Call, Config, Event, Pallet, Storage}, + Timestamp: pallet_timestamp::{Pallet}, + Tokens: orml_tokens::{Config, Event, Pallet, Storage}, Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, + } +); + +#[cfg(not(feature = "with-global-disputes"))] +construct_runtime!( + pub enum Runtime + where + Block = BlockTest, + NodeBlock = BlockTest, + UncheckedExtrinsic = UncheckedExtrinsicTest, + { + Balances: pallet_balances::{Call, Config, Event, Pallet, Storage}, + AssetManager: orml_currencies::{Call, Pallet, Storage}, + MarketCommons: zrml_market_commons::{Pallet, Storage}, + SimpleDisputes: zrml_simple_disputes::{Event, Pallet, Storage}, System: frame_system::{Call, Config, Event, Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet}, + Tokens: orml_tokens::{Config, Event, Pallet, Storage}, + Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, } ); @@ -96,6 +132,8 @@ impl crate::Config for Runtime { type DisputeBond = DisputeBond; type DisputeFactor = DisputeFactor; type DisputeResolution = NoopResolution; + #[cfg(feature = "with-global-disputes")] + type GlobalDisputes = GlobalDisputes; type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; @@ -104,6 +142,21 @@ impl crate::Config for Runtime { type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } +#[cfg(feature = "with-global-disputes")] +impl zrml_global_disputes::Config for Runtime { + type Event = Event; + type MarketCommons = MarketCommons; + type Currency = Balances; + type GlobalDisputeLockId = GlobalDisputeLockId; + type GlobalDisputesPalletId = GlobalDisputesPalletId; + type MaxGlobalDisputeVotes = MaxGlobalDisputeVotes; + type MaxOwners = MaxOwners; + type MinOutcomeVoteAmount = MinOutcomeVoteAmount; + type RemoveKeysLimit = RemoveKeysLimit; + type VotingOutcomeFee = VotingOutcomeFee; + type WeightInfo = zrml_global_disputes::weights::WeightInfo; +} + impl frame_system::Config for Runtime { type AccountData = pallet_balances::AccountData; type AccountId = AccountIdTest; diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index 5b0233045..c256998fc 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -19,7 +19,7 @@ use crate::{ mock::{ExtBuilder, Runtime, SimpleDisputes}, - Error, MarketOf, Disputes, + Disputes, Error, MarketOf, }; use frame_support::{assert_noop, BoundedVec}; use zeitgeist_primitives::{ @@ -76,10 +76,14 @@ fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outc ExtBuilder.build().execute_with(|| { let mut market = DEFAULT_MARKET; market.status = MarketStatus::Disputed; - let disputes = BoundedVec::try_from([ - MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(0) }, - MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(20) }, - ].to_vec()).unwrap(); + let disputes = BoundedVec::try_from( + [ + MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(0) }, + MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(20) }, + ] + .to_vec(), + ) + .unwrap(); Disputes::::insert(&0, &disputes); assert_eq!( &SimpleDisputes::on_resolution(&0, &market).unwrap().unwrap(), From 1f05799c9349444d6d6a199a1dd83ffc59fa75e1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 13 Jan 2023 14:18:31 +0100 Subject: [PATCH 020/368] wip --- runtime/common/src/lib.rs | 9 ++++-- zrml/authorized/src/mock.rs | 4 +-- zrml/authorized/src/tests.rs | 27 ++++------------ zrml/court/src/mock.rs | 6 ++-- zrml/court/src/tests.rs | 30 ++++++++--------- .../fuzz/pm_full_workflow.rs | 1 - zrml/prediction-markets/src/benchmarks.rs | 31 ++++++++++++------ zrml/prediction-markets/src/lib.rs | 5 ++- zrml/prediction-markets/src/mock.rs | 9 ++++-- zrml/prediction-markets/src/tests.rs | 2 +- zrml/simple-disputes/src/lib.rs | 9 +++--- zrml/simple-disputes/src/mock.rs | 32 +++++++++---------- zrml/simple-disputes/src/tests.rs | 2 +- 13 files changed, 85 insertions(+), 82 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 70389df19..ee42b6c10 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -986,8 +986,6 @@ macro_rules! impl_config_traits { type Court = Court; type CloseOrigin = EnsureRootOrTwoThirdsAdvisoryCommittee; type DestroyOrigin = EnsureRootOrAllAdvisoryCommittee; - type DisputeBond = DisputeBond; - type DisputeFactor = DisputeFactor; type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; @@ -1042,10 +1040,17 @@ macro_rules! impl_config_traits { } impl zrml_simple_disputes::Config for Runtime { + type AssetManager = AssetManager; + type DisputeBond = DisputeBond; + type DisputeFactor = DisputeFactor; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type MarketCommons = MarketCommons; + type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; + type PredictionMarketsPalletId = PmPalletId; + type Slash = Treasury; + type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } #[cfg(feature = "with-global-disputes")] diff --git a/zrml/authorized/src/mock.rs b/zrml/authorized/src/mock.rs index 2d7b3014a..adef4d071 100644 --- a/zrml/authorized/src/mock.rs +++ b/zrml/authorized/src/mock.rs @@ -38,8 +38,8 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, - MarketId, Moment, UncheckedExtrinsicTest, + AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, Moment, + UncheckedExtrinsicTest, }, }; diff --git a/zrml/authorized/src/tests.rs b/zrml/authorized/src/tests.rs index 7cd33883c..4c5550000 100644 --- a/zrml/authorized/src/tests.rs +++ b/zrml/authorized/src/tests.rs @@ -26,7 +26,7 @@ use crate::{ use frame_support::{assert_noop, assert_ok, dispatch::DispatchError}; use zeitgeist_primitives::{ traits::DisputeApi, - types::{AuthorityReport, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport}, + types::{AuthorityReport, MarketDisputeMechanism, MarketStatus, OutcomeReport}, }; use zrml_market_commons::Markets; @@ -156,22 +156,10 @@ fn authorize_market_outcome_fails_on_unauthorized_account() { }); } -#[test] -fn on_dispute_fails_if_disputes_is_not_empty() { - ExtBuilder::default().build().execute_with(|| { - let dispute = - MarketDispute { by: crate::mock::ALICE, at: 0, outcome: OutcomeReport::Scalar(1) }; - assert_noop!( - Authorized::on_dispute(&[dispute], &0, &market_mock::()), - Error::::OnlyOneDisputeAllowed - ); - }); -} - #[test] fn on_resolution_fails_if_no_report_was_submitted() { ExtBuilder::default().build().execute_with(|| { - let report = Authorized::on_resolution(&[], &0, &market_mock::()).unwrap(); + let report = Authorized::on_resolution(&0, &market_mock::()).unwrap(); assert!(report.is_none()); }); } @@ -186,7 +174,7 @@ fn on_resolution_removes_stored_outcomes() { 0, OutcomeReport::Scalar(2) )); - assert_ok!(Authorized::on_resolution(&[], &0, &market)); + assert_ok!(Authorized::on_resolution(&0, &market)); assert_eq!(AuthorizedOutcomeReports::::get(0), None); }); } @@ -207,10 +195,7 @@ fn on_resolution_returns_the_reported_outcome() { 0, OutcomeReport::Scalar(2) )); - assert_eq!( - Authorized::on_resolution(&[], &0, &market).unwrap(), - Some(OutcomeReport::Scalar(2)) - ); + assert_eq!(Authorized::on_resolution(&0, &market).unwrap(), Some(OutcomeReport::Scalar(2))); }); } @@ -255,7 +240,7 @@ fn get_auto_resolve_works() { )); let now = frame_system::Pallet::::block_number(); let resolve_at = now + ::CorrectionPeriod::get(); - assert_eq!(Authorized::get_auto_resolve(&[], &0, &market).unwrap(), Some(resolve_at),); + assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap(), Some(resolve_at),); }); } @@ -263,6 +248,6 @@ fn get_auto_resolve_works() { fn get_auto_resolve_returns_none_without_market_storage() { ExtBuilder::default().build().execute_with(|| { let market = market_mock::(); - assert_eq!(Authorized::get_auto_resolve(&[], &0, &market).unwrap(), None,); + assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap(), None,); }); } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 174eeebbc..bc41187cf 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -23,7 +23,7 @@ use frame_support::{ pallet_prelude::{DispatchError, Weight}, parameter_types, traits::Everything, - BoundedVec, PalletId, + PalletId, }; use sp_runtime::{ testing::Header, @@ -36,8 +36,8 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketDispute, - MarketId, Moment, UncheckedExtrinsicTest, + AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, Moment, + UncheckedExtrinsicTest, }, }; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index c2ecc4b32..b20b4ca18 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -130,7 +130,7 @@ fn on_dispute_denies_non_court_markets() { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; assert_noop!( - Court::on_dispute(&[], &0, &market), + Court::on_dispute(&0, &market), Error::::MarketDoesNotHaveCourtMechanism ); }); @@ -142,7 +142,7 @@ fn on_resolution_denies_non_court_markets() { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; assert_noop!( - Court::on_resolution(&[], &0, &market), + Court::on_resolution(&0, &market), Error::::MarketDoesNotHaveCourtMechanism ); }); @@ -154,7 +154,7 @@ fn on_dispute_stores_jurors_that_should_vote() { setup_blocks(123); let _ = Court::join_court(Origin::signed(ALICE)); let _ = Court::join_court(Origin::signed(BOB)); - Court::on_dispute(&[], &0, &DEFAULT_MARKET).unwrap(); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); assert_noop!( Court::join_court(Origin::signed(ALICE)), Error::::JurorAlreadyExists @@ -171,11 +171,11 @@ fn on_resolution_awards_winners_and_slashes_losers() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&[], &0, &DEFAULT_MARKET).unwrap(); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(2)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(3)).unwrap(); - let _ = Court::on_resolution(&[], &0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Balances::free_balance(ALICE), 998 * BASE + 3 * BASE); assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), 2 * BASE); assert_eq!(Balances::free_balance(BOB), 996 * BASE); @@ -192,11 +192,11 @@ fn on_resolution_decides_market_outcome_based_on_the_majority() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&[], &0, &DEFAULT_MARKET).unwrap(); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let outcome = Court::on_resolution(&[], &0, &DEFAULT_MARKET).unwrap(); + let outcome = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(outcome, Some(OutcomeReport::Scalar(1))); }); } @@ -208,8 +208,8 @@ fn on_resolution_sets_late_jurors_as_tardy() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::on_dispute(&[], &0, &DEFAULT_MARKET).unwrap(); - let _ = Court::on_resolution(&[], &0, &DEFAULT_MARKET).unwrap(); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(ALICE).unwrap().status, JurorStatus::Ok); assert_eq!(Jurors::::get(BOB).unwrap().status, JurorStatus::Tardy); }); @@ -222,11 +222,11 @@ fn on_resolution_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tard Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&[], &0, &DEFAULT_MARKET).unwrap(); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::on_resolution(&[], &0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(CHARLIE).unwrap().status, JurorStatus::Tardy); }); } @@ -239,8 +239,8 @@ fn on_resolution_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { Court::join_court(Origin::signed(BOB)).unwrap(); Court::set_stored_juror_as_tardy(&BOB).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::on_dispute(&[], &0, &DEFAULT_MARKET).unwrap(); - let _ = Court::on_resolution(&[], &0, &DEFAULT_MARKET).unwrap(); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); let join_court_stake = 40000000000; let slash = join_court_stake / 5; assert_eq!(Balances::free_balance(Court::treasury_account_id()), INITIAL_BALANCE + slash); @@ -256,11 +256,11 @@ fn on_resolution_removes_requested_jurors_and_votes() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&[], &0, &DEFAULT_MARKET).unwrap(); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::on_resolution(&[], &0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(RequestedJurors::::iter().count(), 0); assert_eq!(Votes::::iter().count(), 0); }); diff --git a/zrml/prediction-markets/fuzz/pm_full_workflow.rs b/zrml/prediction-markets/fuzz/pm_full_workflow.rs index e6025f9f5..cc9ba7879 100644 --- a/zrml/prediction-markets/fuzz/pm_full_workflow.rs +++ b/zrml/prediction-markets/fuzz/pm_full_workflow.rs @@ -74,7 +74,6 @@ fuzz_target!(|data: Data| { let _ = PredictionMarkets::dispute( Origin::signed(data.report_origin.into()), dispute_market_id, - outcome(data.report_outcome), ); let _ = PredictionMarkets::on_initialize(5); diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index bd78f52fb..b9b07568d 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -230,8 +230,6 @@ benchmarks! { admin_destroy_disputed_market{ // The number of assets. let a in (T::MinCategories::get().into())..T::MaxCategories::get().into(); - // The number of disputes. - let d in 1..T::MaxDisputes::get(); // The number of market ids per open time frame. let o in 0..63; // The number of market ids per close time frame. @@ -244,6 +242,11 @@ benchmarks! { OutcomeReport::Categorical(0u16), )?; + >::mutate_market(&market_id, |market| { + market.dispute_mechanism = MarketDisputeMechanism::Authorized; + Ok(()) + })?; + let pool_id = >::market_pool(&market_id)?; let disputor = account("disputor", 1, 0); @@ -270,9 +273,14 @@ benchmarks! { ).unwrap(); } - let disputes = Disputes::::get(market_id); - let last_dispute = disputes.last().unwrap(); - let resolves_at = last_dispute.at.saturating_add(market.deadlines.dispute_duration); + AuthorizedPallet::::authorize_market_outcome( + T::AuthorizedDisputeResolutionOrigin::successful_origin(), + market_id.into(), + OutcomeReport::Scalar(0), + )?; + + let now = >::block_number(); + let resolves_at = now.saturating_add(::CorrectionPeriod::get()); for i in 0..r { MarketIdsPerDisputeBlock::::try_mutate( resolves_at, @@ -781,23 +789,26 @@ benchmarks! { let _ = Call::::dispute { market_id, } - .dispatch_bypass_filter(RawOrigin::Signed(disputor.clone()).into())?; + .dispatch_bypass_filter(RawOrigin::Signed(disputor).into())?; let max_dispute_len = ::MaxDisputes::get(); for i in 0..max_dispute_len { // ensure that the MarketIdsPerDisputeBlock does not interfere // with the start_global_dispute execution block >::set_block_number(i.saturated_into()); - let reserver: T::AccountId = account("Reserver", i, 0); - T::AssetManager::deposit(Asset::Ztg, &reserver, (u128::MAX).saturated_into())?; + let reserver: T::AccountId = account("Reserver", i, 0); + ::AssetManager::deposit(Asset::Ztg, &reserver, (u128::MAX).saturated_into())?; + let market_id_number: u128 = market_id.saturated_into::(); let _ = zrml_simple_disputes::Call::::reserve_outcome { - market_id, + market_id: market_id_number.saturated_into(), outcome: OutcomeReport::Scalar(i.saturated_into()), }.dispatch_bypass_filter(RawOrigin::Signed(reserver.clone()).into())?; } let market = >::market(&market_id.saturated_into()).unwrap(); - let disputes = zrml_simple_disputes::Disputes::::get(market_id); + let market_id_number: u128 = market_id.saturated_into::(); + let market_id_simple: zrml_simple_disputes::MarketIdOf = market_id_number.saturated_into(); + let disputes = zrml_simple_disputes::Disputes::::get(market_id_simple); let last_dispute = disputes.last().unwrap(); let dispute_duration_ends_at_block = last_dispute.at + market.deadlines.dispute_duration; let mut market_ids_2: BoundedVec, CacheSize> = BoundedVec::try_from( diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 141c32000..52c0c6cf5 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -520,9 +520,8 @@ mod pallet { origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; + ensure_signed(origin)?; - let curr_block_num = >::block_number(); let market = >::market(&market_id)?; ensure!(market.status == MarketStatus::Reported, Error::::InvalidMarketStatus); @@ -2336,7 +2335,7 @@ mod pallet { if report.by == market.oracle && report.outcome == resolved_outcome { Self::unreserve_oracle_bond(market_id)?; } else { - let imbalance = Self::slash_oracle_bond(market_id, None)?; + let _imbalance = Self::slash_oracle_bond(market_id, None)?; // TODO what should be done with the OracleBond imbalance? // overall_imbalance.subsume(imbalance); } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 988ca53b4..4476b3eb8 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -458,16 +458,19 @@ mod tests { ); assert_ne!( ::AdvisoryBond::get(), - ::DisputeBond::get() + ::DisputeBond::get() ); assert_ne!( ::OracleBond::get(), ::ValidityBond::get() ); - assert_ne!(::OracleBond::get(), ::DisputeBond::get()); + assert_ne!( + ::OracleBond::get(), + ::DisputeBond::get() + ); assert_ne!( ::ValidityBond::get(), - ::DisputeBond::get() + ::DisputeBond::get() ); } } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index d41136a57..c0bd6044a 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -19,7 +19,7 @@ #![allow(clippy::reversed_empty_ranges)] use crate::{ - default_dispute_bond, mock::*, Config, Disputes, Error, Event, LastTimeFrame, MarketIdsForEdit, + mock::*, Config, Disputes, Error, Event, LastTimeFrame, MarketIdsForEdit, MarketIdsPerCloseBlock, MarketIdsPerDisputeBlock, MarketIdsPerOpenBlock, MarketIdsPerReportBlock, }; diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 040ef8dda..7902d1b98 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -31,6 +31,7 @@ pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; #[frame_support::pallet] mod pallet { use crate::{weights::WeightInfoZeitgeist, SimpleDisputesPalletApi}; + use alloc::vec::Vec; use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, @@ -39,11 +40,12 @@ mod pallet { traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency, OnUnbalanced}, transactional, BoundedVec, PalletId, }; - use alloc::vec::Vec; use frame_system::pallet_prelude::*; use orml_traits::currency::NamedMultiReservableCurrency; + #[cfg(feature = "with-global-disputes")] + use sp_runtime::traits::Zero; use sp_runtime::{ - traits::{CheckedDiv, Saturating, Zero}, + traits::{CheckedDiv, Saturating}, DispatchError, SaturatedConversion, }; use zeitgeist_primitives::{ @@ -124,8 +126,7 @@ mod pallet { <::MarketCommons as MarketCommonsPalletApi>::Currency; pub(crate) type NegativeImbalanceOf = as Currency<::AccountId>>::NegativeImbalance; - pub(crate) type MarketIdOf = - <::MarketCommons as MarketCommonsPalletApi>::MarketId; + pub type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; pub(crate) type MarketOf = Market< ::AccountId, diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 16a23d2a7..59ba06cb3 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -19,34 +19,34 @@ use crate::{self as zrml_simple_disputes}; use frame_support::{ - construct_runtime, + construct_runtime, ord_parameter_types, pallet_prelude::{DispatchError, Weight}, - traits::Everything, + traits::{Everything, NeverEnsureOrigin}, }; use frame_system::EnsureSignedBy; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; -use frame_support::ord_parameter_types; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, DisputeBond, DisputeFactor, MaxDisputes, MaxReserves, MinimumPeriod, - PmPalletId, SimpleDisputesPalletId, GetNativeCurrencyId, MaxApprovals, TreasuryPalletId, + BlockHashCount, DisputeBond, DisputeFactor, ExistentialDeposits, GetNativeCurrencyId, + MaxApprovals, MaxDisputes, MaxReserves, MinimumPeriod, PmPalletId, SimpleDisputesPalletId, + TreasuryPalletId, }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, Market, - MarketId, Moment, UncheckedExtrinsicTest, BasicCurrencyAdapter, + AccountIdTest, Amount, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, CurrencyId, + Hash, Index, Market, MarketId, Moment, UncheckedExtrinsicTest, }, }; -pub const ALICE: AccountIdTest = 0; -pub const BOB: AccountIdTest = 1; -pub const CHARLIE: AccountIdTest = 2; -pub const DAVE: AccountIdTest = 3; -pub const EVE: AccountIdTest = 4; -pub const FRED: AccountIdTest = 5; +#[cfg(feature = "with-global-disputes")] +use zeitgeist_primitives::constants::mock::{ + GlobalDisputeLockId, GlobalDisputesPalletId, MaxGlobalDisputeVotes, MaxOwners, + MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, +}; + pub const SUDO: AccountIdTest = 69; ord_parameter_types! { @@ -144,7 +144,7 @@ impl crate::Config for Runtime { #[cfg(feature = "with-global-disputes")] impl zrml_global_disputes::Config for Runtime { - type Event = Event; + type Event = (); type MarketCommons = MarketCommons; type Currency = Balances; type GlobalDisputeLockId = GlobalDisputeLockId; @@ -208,7 +208,7 @@ impl orml_tokens::Config for Runtime { type Balance = Balance; type CurrencyId = CurrencyId; type DustRemovalWhitelist = Everything; - type Event = Event; + type Event = (); type ExistentialDeposits = ExistentialDeposits; type MaxLocks = (); type MaxReserves = MaxReserves; @@ -238,7 +238,7 @@ impl pallet_treasury::Config for Runtime { type Burn = (); type BurnDestination = (); type Currency = Balances; - type Event = Event; + type Event = (); type MaxApprovals = MaxApprovals; type OnSlash = (); type PalletId = TreasuryPalletId; diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index c256998fc..a62d21549 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -84,7 +84,7 @@ fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outc .to_vec(), ) .unwrap(); - Disputes::::insert(&0, &disputes); + Disputes::::insert(0, &disputes); assert_eq!( &SimpleDisputes::on_resolution(&0, &market).unwrap().unwrap(), &disputes.last().unwrap().outcome From 0b80b7d9aad8069d939039717e1011971e9a08aa Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 13 Jan 2023 15:40:24 +0100 Subject: [PATCH 021/368] fix tests --- runtime/common/src/lib.rs | 2 + zrml/prediction-markets/Cargo.toml | 1 + zrml/prediction-markets/src/benchmarks.rs | 2 +- zrml/prediction-markets/src/tests.rs | 133 ++++++++++------------ zrml/simple-disputes/src/lib.rs | 2 +- 5 files changed, 66 insertions(+), 74 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index ee42b6c10..1eb054835 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1045,6 +1045,8 @@ macro_rules! impl_config_traits { type DisputeFactor = DisputeFactor; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; + #[cfg(feature = "with-global-disputes")] + type GlobalDisputes = GlobalDisputes; type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; diff --git a/zrml/prediction-markets/Cargo.toml b/zrml/prediction-markets/Cargo.toml index 0720f9b84..67a3b6d8d 100644 --- a/zrml/prediction-markets/Cargo.toml +++ b/zrml/prediction-markets/Cargo.toml @@ -80,6 +80,7 @@ try-runtime = [ ] with-global-disputes = [ "zrml-global-disputes", + "zrml-simple-disputes/with-global-disputes", ] [package] diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index b9b07568d..622bad072 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -276,7 +276,7 @@ benchmarks! { AuthorizedPallet::::authorize_market_outcome( T::AuthorizedDisputeResolutionOrigin::successful_origin(), market_id.into(), - OutcomeReport::Scalar(0), + OutcomeReport::Categorical(0u16), )?; let now = >::block_number(); diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index c0bd6044a..dff638bbc 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -42,7 +42,6 @@ use zeitgeist_primitives::{ MultiHash, OutcomeReport, PoolStatus, ScalarPosition, ScoringRule, }, }; -use zrml_authorized::Error as AuthorizedError; use zrml_market_commons::MarketCommonsPalletApi; use zrml_swaps::Pools; @@ -407,11 +406,7 @@ fn admin_destroy_market_correctly_slashes_permissionless_market_disputed() { OutcomeReport::Categorical(1) )); run_to_block(grace_period + 2); - assert_ok!(PredictionMarkets::dispute( - Origin::signed(CHARLIE), - 0, - OutcomeReport::Categorical(0) - )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); assert_ok!(AssetManager::deposit(Asset::Ztg, &ALICE, 2 * SENTINEL_AMOUNT)); assert_ok!(Balances::reserve_named( &PredictionMarkets::reserve_id(), @@ -445,12 +440,13 @@ fn admin_destroy_market_correctly_unreserves_dispute_bonds() { OutcomeReport::Categorical(1) )); run_to_block(grace_period + 2); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) )); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(DAVE), 0, OutcomeReport::Categorical(1) @@ -479,11 +475,11 @@ fn admin_destroy_market_correctly_unreserves_dispute_bonds() { ); assert_eq!( Balances::free_balance(CHARLIE), - balance_free_before_charlie + default_dispute_bond::(0) + balance_free_before_charlie + zrml_simple_disputes::default_dispute_bond::(0) ); assert_eq!( Balances::free_balance(DAVE), - balance_free_before_dave + default_dispute_bond::(1), + balance_free_before_dave + zrml_simple_disputes::default_dispute_bond::(1), ); assert!(Disputes::::get(market_id).is_empty()); }); @@ -638,11 +634,7 @@ fn admin_destroy_market_correctly_slashes_advised_market_disputed() { 0, OutcomeReport::Categorical(1) )); - assert_ok!(PredictionMarkets::dispute( - Origin::signed(CHARLIE), - 0, - OutcomeReport::Categorical(0) - )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); assert_ok!(AssetManager::deposit(Asset::Ztg, &ALICE, 2 * SENTINEL_AMOUNT)); assert_ok!(Balances::reserve_named( &PredictionMarkets::reserve_id(), @@ -2053,7 +2045,8 @@ fn it_allows_to_dispute_the_outcome_of_a_market() { let dispute_at = grace_period + 2; run_to_block(dispute_at); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) @@ -2062,7 +2055,7 @@ fn it_allows_to_dispute_the_outcome_of_a_market() { let market = MarketCommons::market(&0).unwrap(); assert_eq!(market.status, MarketStatus::Disputed); - let disputes = crate::Disputes::::get(0); + let disputes = zrml_simple_disputes::Disputes::::get(0); assert_eq!(disputes.len(), 1); let dispute = &disputes[0]; assert_eq!(dispute.at, dispute_at); @@ -2077,7 +2070,7 @@ fn it_allows_to_dispute_the_outcome_of_a_market() { } #[test] -fn dispute_fails_authority_reported_already() { +fn dispute_fails_disputed_already() { ExtBuilder::default().build().execute_with(|| { let end = 2; assert_ok!(PredictionMarkets::create_market( @@ -2106,15 +2099,11 @@ fn dispute_fails_authority_reported_already() { let dispute_at = grace_period + 2; run_to_block(dispute_at); - assert_ok!(PredictionMarkets::dispute( - Origin::signed(CHARLIE), - 0, - OutcomeReport::Categorical(0) - )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); assert_noop!( - PredictionMarkets::dispute(Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(1)), - AuthorizedError::::OnlyOneDisputeAllowed + PredictionMarkets::dispute(Origin::signed(CHARLIE), 0), + Error::::InvalidMarketStatus, ); }); } @@ -2223,10 +2212,12 @@ fn it_resolves_a_disputed_market() { OutcomeReport::Categorical(0) )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + let dispute_at_0 = report_at + 1; run_to_block(dispute_at_0); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(1) @@ -2235,7 +2226,7 @@ fn it_resolves_a_disputed_market() { let dispute_at_1 = report_at + 2; run_to_block(dispute_at_1); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(DAVE), 0, OutcomeReport::Categorical(0) @@ -2244,7 +2235,7 @@ fn it_resolves_a_disputed_market() { let dispute_at_2 = report_at + 3; run_to_block(dispute_at_2); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) @@ -2264,7 +2255,7 @@ fn it_resolves_a_disputed_market() { assert_eq!(eve_reserved, DisputeBond::get() + 2 * DisputeFactor::get()); // check disputes length - let disputes = crate::Disputes::::get(0); + let disputes = zrml_simple_disputes::Disputes::::get(0); assert_eq!(disputes.len(), 3); // make sure the old mappings of market id per dispute block are erased @@ -2287,7 +2278,7 @@ fn it_resolves_a_disputed_market() { let market_after = MarketCommons::market(&0).unwrap(); assert_eq!(market_after.status, MarketStatus::Resolved); - let disputes = crate::Disputes::::get(0); + let disputes = zrml_simple_disputes::Disputes::::get(0); assert_eq!(disputes.len(), 0); assert_ok!(PredictionMarkets::redeem_shares(Origin::signed(CHARLIE), 0)); @@ -2343,7 +2334,7 @@ fn dispute_fails_unless_reported_or_disputed_market(status: MarketStatus) { })); assert_noop!( - PredictionMarkets::dispute(Origin::signed(EVE), 0, OutcomeReport::Categorical(1)), + PredictionMarkets::dispute(Origin::signed(EVE), 0), Error::::InvalidMarketStatus ); }); @@ -2376,31 +2367,25 @@ fn start_global_dispute_works() { )); let dispute_at_0 = end + grace_period + 2; run_to_block(dispute_at_0); - for i in 1..=::MaxDisputes::get() { - if i == 1 { - #[cfg(feature = "with-global-disputes")] - assert_noop!( - PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), - Error::::InvalidMarketStatus - ); - } else { - #[cfg(feature = "with-global-disputes")] - assert_noop!( - PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), - Error::::MaxDisputesNeeded - ); - } - assert_ok!(PredictionMarkets::dispute( + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); + for i in 1..=::MaxDisputes::get() { + #[cfg(feature = "with-global-disputes")] + assert_noop!( + PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), + Error::::MarketDisputeMechanismNotFailed + ); + + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), market_id, - OutcomeReport::Categorical(i.saturated_into()) + OutcomeReport::Categorical(i.saturated_into()), )); run_blocks(1); let market = MarketCommons::market(&market_id).unwrap(); assert_eq!(market.status, MarketStatus::Disputed); } - let disputes = crate::Disputes::::get(market_id); + let disputes = zrml_simple_disputes::Disputes::::get(market_id); assert_eq!(disputes.len(), ::MaxDisputes::get() as usize); let last_dispute = disputes.last().unwrap(); @@ -2421,7 +2406,8 @@ fn start_global_dispute_works() { Some((Zero::zero(), vec![BOB])), ); for i in 1..=::MaxDisputes::get() { - let dispute_bond = crate::default_dispute_bond::((i - 1).into()); + let dispute_bond = + zrml_simple_disputes::default_dispute_bond::((i - 1).into()); assert_eq!( GlobalDisputes::get_voting_outcome_info( &market_id, @@ -2479,11 +2465,7 @@ fn start_global_dispute_fails_on_wrong_mdm() { run_to_block(dispute_at_0); // only one dispute allowed for authorized mdm - assert_ok!(PredictionMarkets::dispute( - Origin::signed(CHARLIE), - market_id, - OutcomeReport::Categorical(1u32.saturated_into()) - )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); run_blocks(1); let market = MarketCommons::market(&market_id).unwrap(); assert_eq!(market.status, MarketStatus::Disputed); @@ -2879,15 +2861,20 @@ fn full_scalar_market_lifecycle() { assert_eq!(report.outcome, OutcomeReport::Scalar(100)); // dispute - assert_ok!(PredictionMarkets::dispute(Origin::signed(DAVE), 0, OutcomeReport::Scalar(25))); - let disputes = crate::Disputes::::get(0); + assert_ok!(PredictionMarkets::dispute(Origin::signed(DAVE), 0)); + assert_ok!(SimpleDisputes::reserve_outcome( + Origin::signed(DAVE), + 0, + OutcomeReport::Scalar(25) + )); + let disputes = zrml_simple_disputes::Disputes::::get(0); assert_eq!(disputes.len(), 1); run_blocks(market.deadlines.dispute_duration); let market_after_resolve = MarketCommons::market(&0).unwrap(); assert_eq!(market_after_resolve.status, MarketStatus::Resolved); - let disputes = crate::Disputes::::get(0); + let disputes = zrml_simple_disputes::Disputes::::get(0); assert_eq!(disputes.len(), 0); // give EVE some shares @@ -3064,11 +3051,7 @@ fn authorized_correctly_resolves_disputed_market() { let dispute_at = grace_period + 1 + 1; run_to_block(dispute_at); - assert_ok!(PredictionMarkets::dispute( - Origin::signed(CHARLIE), - 0, - OutcomeReport::Categorical(1) - )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); let charlie_balance = Balances::free_balance(&CHARLIE); assert_eq!(charlie_balance, 1_000 * BASE - CENT - DisputeBond::get()); @@ -3482,7 +3465,8 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark 0, OutcomeReport::Categorical(0) )); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(1) @@ -3528,7 +3512,8 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_approved_advised_ma 0, OutcomeReport::Categorical(0) )); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(1) @@ -3576,13 +3561,14 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark 0, OutcomeReport::Categorical(0) )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) @@ -3631,13 +3617,14 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma 0, OutcomeReport::Categorical(0) )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) @@ -3687,13 +3674,14 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark 0, OutcomeReport::Categorical(0) )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(FRED), 0, OutcomeReport::Categorical(0) @@ -3741,13 +3729,14 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma 0, OutcomeReport::Categorical(0) )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(PredictionMarkets::dispute( + assert_ok!(SimpleDisputes::reserve_outcome( Origin::signed(FRED), 0, OutcomeReport::Categorical(0) diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 7902d1b98..b49cbc4d9 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -443,7 +443,7 @@ mod pallet { impl SimpleDisputesPalletApi for Pallet where T: Config {} // No-one can bound more than BalanceOf, therefore, this functions saturates - pub(crate) fn default_dispute_bond(n: usize) -> BalanceOf + pub fn default_dispute_bond(n: usize) -> BalanceOf where T: Config, { From 6236d510a52e603455abf54e02a4607c5c361758 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 16 Jan 2023 09:28:18 +0100 Subject: [PATCH 022/368] taplo fmt --- zrml/simple-disputes/Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zrml/simple-disputes/Cargo.toml b/zrml/simple-disputes/Cargo.toml index 59b787812..e7c02f8d3 100644 --- a/zrml/simple-disputes/Cargo.toml +++ b/zrml/simple-disputes/Cargo.toml @@ -2,20 +2,20 @@ frame-benchmarking = { branch = "polkadot-v0.9.26", default-features = false, optional = true, git = "https://github.com/paritytech/substrate" } frame-support = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/paritytech/substrate" } frame-system = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/paritytech/substrate" } +orml-traits = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library" } parity-scale-codec = { default-features = false, features = ["derive", "max-encoded-len"], version = "3.0.0" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-runtime = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/paritytech/substrate" } zeitgeist-primitives = { default-features = false, path = "../../primitives" } -zrml-market-commons = { default-features = false, path = "../market-commons" } -orml-traits = { branch = "polkadot-v0.9.26", default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library" } zrml-global-disputes = { default-features = false, path = "../global-disputes", optional = true } +zrml-market-commons = { default-features = false, path = "../market-commons" } [dev-dependencies] -orml-currencies = { branch = "polkadot-v0.9.26", git = "https://github.com/open-web3-stack/open-runtime-module-library"} -orml-tokens = { branch = "polkadot-v0.9.26", git = "https://github.com/open-web3-stack/open-runtime-module-library"} +orml-currencies = { branch = "polkadot-v0.9.26", git = "https://github.com/open-web3-stack/open-runtime-module-library" } +orml-tokens = { branch = "polkadot-v0.9.26", git = "https://github.com/open-web3-stack/open-runtime-module-library" } pallet-balances = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } -pallet-treasury = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } pallet-timestamp = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } +pallet-treasury = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } sp-io = { branch = "polkadot-v0.9.26", git = "https://github.com/paritytech/substrate" } zeitgeist-primitives = { default-features = false, features = ["mock"], path = "../../primitives" } From e4a09a98d993096ba8e3eaf3b9666febd77bac0e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 16 Jan 2023 12:27:58 +0100 Subject: [PATCH 023/368] update court authorized mdms --- zrml/authorized/src/lib.rs | 16 ++++++++++--- zrml/court/src/lib.rs | 49 +++++++++++++++++++++++++++++--------- zrml/court/src/tests.rs | 30 ++++++++++++++--------- 3 files changed, 70 insertions(+), 25 deletions(-) diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index ea4fd70ae..35e742264 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -96,16 +96,21 @@ mod pallet { let report_opt = AuthorizedOutcomeReports::::get(market_id); let (report, ids_len) = match &report_opt { - Some(report) => (AuthorityReport { resolve_at: report.resolve_at, outcome }, 0u32), + Some(report) => ( + AuthorityReport { resolve_at: report.resolve_at, outcome: outcome.clone() }, + 0u32, + ), None => { let resolve_at = now.saturating_add(T::CorrectionPeriod::get()); let ids_len = T::DisputeResolution::add_auto_resolve(&market_id, resolve_at)?; - (AuthorityReport { resolve_at, outcome }, ids_len) + (AuthorityReport { resolve_at, outcome: outcome.clone() }, ids_len) } }; AuthorizedOutcomeReports::::insert(market_id, report); + Self::deposit_event(Event::AuthorityReported { market_id, outcome }); + if report_opt.is_none() { Ok(Some(T::WeightInfo::authorize_market_outcome_first_report(ids_len)).into()) } else { @@ -158,9 +163,14 @@ mod pallet { } #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event where - T: Config, {} + T: Config, + { + /// The Authority reported. + AuthorityReported { market_id: MarketIdOf, outcome: OutcomeReport }, + } #[pallet::hooks] impl Hooks for Pallet {} diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 22f8180c1..c667a7fb9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -66,7 +66,7 @@ mod pallet { }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, - types::{Market, MarketDispute, MarketDisputeMechanism, OutcomeReport}, + types::{Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport}, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -98,6 +98,36 @@ mod pallet { #[pallet::call] impl Pallet { + #[pallet::weight(1_000_000_000_000)] + pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + // TODO take a bond from the caller + ensure_signed(origin)?; + let market = T::MarketCommons::market(&market_id)?; + ensure!( + market.status == MarketStatus::Disputed, + Error::::MarketIsNotDisputed + ); + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + Error::::MarketDoesNotHaveCourtMechanism + ); + + let jurors: Vec<_> = Jurors::::iter().collect(); + // TODO &[] was disputes list before: how to handle it now without disputes from pm? + let necessary_jurors_num = Self::necessary_jurors_num(&[]); + let mut rng = Self::rng(); + let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); + let curr_block_num = >::block_number(); + let block_limit = curr_block_num.saturating_add(T::CourtCaseDuration::get()); + for (ai, _) in random_jurors { + RequestedJurors::::insert(market_id, ai, block_limit); + } + + Self::deposit_event(Event::MarketAppealed { market_id }); + + Ok(()) + } + // MARK(non-transactional): `remove_juror_from_all_courts_of_all_markets` is infallible. #[pallet::weight(T::WeightInfo::exit_court())] pub fn exit_court(origin: OriginFor) -> DispatchResult { @@ -198,6 +228,8 @@ mod pallet { NoVotes, /// Forbids voting of unknown accounts OnlyJurorsCanVote, + /// The market is not in a state where it can be disputed. + MarketIsNotDisputed, } #[pallet::event] @@ -208,6 +240,10 @@ mod pallet { { ExitedJuror(T::AccountId, Juror), JoinedJuror(T::AccountId, Juror), + /// A market has been appealed. + MarketAppealed { + market_id: MarketIdOf, + }, } #[pallet::hooks] @@ -514,16 +550,7 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - let jurors: Vec<_> = Jurors::::iter().collect(); - // TODO &[] was disputes list before: how to handle it now without disputes from pm? - let necessary_jurors_num = Self::necessary_jurors_num(&[]); - let mut rng = Self::rng(); - let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); - let curr_block_num = >::block_number(); - let block_limit = curr_block_num.saturating_add(T::CourtCaseDuration::get()); - for (ai, _) in random_jurors { - RequestedJurors::::insert(market_id, ai, block_limit); - } + Ok(()) } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index b20b4ca18..5d74739ae 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -19,8 +19,8 @@ use crate::{ mock::{ - Balances, Court, ExtBuilder, Origin, RandomnessCollectiveFlip, Runtime, System, ALICE, BOB, - CHARLIE, INITIAL_BALANCE, + Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, + System, ALICE, BOB, CHARLIE, INITIAL_BALANCE, }, Error, Juror, JurorStatus, Jurors, MarketOf, RequestedJurors, Votes, }; @@ -36,6 +36,7 @@ use zeitgeist_primitives::{ MarketStatus, MarketType, OutcomeReport, ScoringRule, }, }; +use zrml_market_commons::MarketCommonsPalletApi; const DEFAULT_MARKET: MarketOf = Market { creation: MarketCreation::Permissionless, @@ -49,7 +50,7 @@ const DEFAULT_MARKET: MarketOf = Market { deadlines: Deadlines { grace_period: 1_u64, oracle_duration: 1_u64, dispute_duration: 1_u64 }, report: None, resolved_outcome: None, - status: MarketStatus::Closed, + status: MarketStatus::Disputed, scoring_rule: ScoringRule::CPMM, bonds: MarketBonds { creation: None, oracle: None }, }; @@ -149,12 +150,13 @@ fn on_resolution_denies_non_court_markets() { } #[test] -fn on_dispute_stores_jurors_that_should_vote() { +fn appeal_stores_jurors_that_should_vote() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); let _ = Court::join_court(Origin::signed(ALICE)); let _ = Court::join_court(Origin::signed(BOB)); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + Court::appeal(Origin::signed(ALICE), 0).unwrap(); assert_noop!( Court::join_court(Origin::signed(ALICE)), Error::::JurorAlreadyExists @@ -171,7 +173,8 @@ fn on_resolution_awards_winners_and_slashes_losers() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(2)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(3)).unwrap(); @@ -192,7 +195,8 @@ fn on_resolution_decides_market_outcome_based_on_the_majority() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); @@ -208,7 +212,8 @@ fn on_resolution_sets_late_jurors_as_tardy() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + Court::appeal(Origin::signed(ALICE), 0).unwrap(); let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(ALICE).unwrap().status, JurorStatus::Ok); assert_eq!(Jurors::::get(BOB).unwrap().status, JurorStatus::Tardy); @@ -222,7 +227,8 @@ fn on_resolution_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tard Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); @@ -239,7 +245,8 @@ fn on_resolution_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { Court::join_court(Origin::signed(BOB)).unwrap(); Court::set_stored_juror_as_tardy(&BOB).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + Court::appeal(Origin::signed(ALICE), 0).unwrap(); let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); let join_court_stake = 40000000000; let slash = join_court_stake / 5; @@ -256,7 +263,8 @@ fn on_resolution_removes_requested_jurors_and_votes() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); From a67c50d7204446f859afe65d1da4e668d84b9169 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 16 Jan 2023 13:44:55 +0100 Subject: [PATCH 024/368] add dispute bond to market storage --- primitives/src/constants/mock.rs | 5 ++- primitives/src/market.rs | 18 ++++++-- runtime/battery-station/src/parameters.rs | 14 +++--- runtime/common/src/lib.rs | 5 ++- runtime/zeitgeist/src/parameters.rs | 14 +++--- zrml/court/src/lib.rs | 11 +++-- zrml/court/src/tests.rs | 2 +- zrml/market-commons/src/tests.rs | 2 +- zrml/prediction-markets/src/lib.rs | 51 +++++++++++++--------- zrml/prediction-markets/src/migrations.rs | 8 +++- zrml/prediction-markets/src/mock.rs | 25 +++++------ zrml/prediction-markets/src/tests.rs | 4 +- zrml/simple-disputes/src/lib.rs | 52 +++++++++-------------- zrml/simple-disputes/src/mock.rs | 6 +-- zrml/simple-disputes/src/tests.rs | 17 ++++++-- 15 files changed, 138 insertions(+), 96 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 515924b50..5295c53ea 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -40,8 +40,7 @@ parameter_types! { // Prediction Market parameters parameter_types! { pub const AdvisoryBond: Balance = 25 * CENT; - pub const DisputeBond: Balance = 5 * BASE; - pub const DisputeFactor: Balance = 2 * BASE; + pub const DisputeBond: Balance = 20 * BASE; pub const GlobalDisputePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; pub const MaxCategories: u16 = 10; pub const MaxDisputes: u16 = 6; @@ -66,6 +65,8 @@ parameter_types! { // Simple disputes parameters parameter_types! { pub const SimpleDisputesPalletId: PalletId = PalletId(*b"zge/sedp"); + pub const OutcomeBond: Balance = 5 * BASE; + pub const OutcomeFactor: Balance = 2 * BASE; } // Swaps parameters diff --git a/primitives/src/market.rs b/primitives/src/market.rs index 9584fd274..450478e97 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -83,6 +83,7 @@ impl Bond { pub struct MarketBonds { pub creation: Option>, pub oracle: Option>, + pub dispute: Option>, } impl MarketBonds { @@ -92,14 +93,16 @@ impl MarketBonds { Some(bond) if bond.who == *who => bond.value, _ => BA::zero(), }; - value_or_default(&self.creation).saturating_add(value_or_default(&self.oracle)) + value_or_default(&self.creation) + .saturating_add(value_or_default(&self.oracle)) + .saturating_add(value_or_default(&self.dispute)) } } // Used primarily for testing purposes. impl Default for MarketBonds { fn default() -> Self { - MarketBonds { creation: None, oracle: None } + MarketBonds { creation: None, oracle: None, dispute: None } } } @@ -166,13 +169,22 @@ pub enum MarketCreation { Advised, } +// TODO to remove, when Disputes storage item is removed #[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct MarketDispute { +pub struct OldMarketDispute { pub at: BlockNumber, pub by: AccountId, pub outcome: OutcomeReport, } +#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +pub struct MarketDispute { + pub at: BlockNumber, + pub by: AccountId, + pub outcome: OutcomeReport, + pub bond: Balance, +} + /// How a market should resolve disputes #[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub enum MarketDisputeMechanism { diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index ed4fd4d2d..ab693358e 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -149,11 +149,9 @@ parameter_types! { pub const AdvisoryBond: Balance = 25 * CENT; pub const AdvisoryBondSlashPercentage: Percent = Percent::from_percent(0); /// (Slashable) Bond that is provided for disputing the outcome. - /// Slashed in case the final outcome does not match the dispute for which the `DisputeBond` - /// was deposited. - pub const DisputeBond: Balance = 5 * BASE; - /// `DisputeBond` is increased by this factor after every dispute. - pub const DisputeFactor: Balance = 2 * BASE; + /// Unreserved in case the dispute was justified otherwise slashed. + /// This is when the resolved outcome is different to the default (reported) outcome. + pub const DisputeBond: Balance = 25 * BASE; /// Maximum Categories a prediciton market can have (excluding base asset). pub const MaxCategories: u16 = MAX_CATEGORIES; /// Maximum number of disputes. @@ -217,6 +215,12 @@ parameter_types! { // Simple disputes parameters /// Pallet identifier, mainly used for named balance reserves. pub const SimpleDisputesPalletId: PalletId = SD_PALLET_ID; + /// (Slashable) Bond that is provided for overriding the last outcome addition. + /// Slashed in case the final outcome does not match the dispute for which the `OutcomeBond` + /// was deposited. + pub const OutcomeBond: Balance = 5 * BASE; + /// `OutcomeBond` is increased by this factor after every new outcome addition. + pub const OutcomeFactor: Balance = 2 * BASE; // Swaps parameters /// A precentage from the withdrawal amount a liquidity provider wants to withdraw diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 1eb054835..d52f9189d 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -986,6 +986,7 @@ macro_rules! impl_config_traits { type Court = Court; type CloseOrigin = EnsureRootOrTwoThirdsAdvisoryCommittee; type DestroyOrigin = EnsureRootOrAllAdvisoryCommittee; + type DisputeBond = DisputeBond; type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; @@ -1041,8 +1042,8 @@ macro_rules! impl_config_traits { impl zrml_simple_disputes::Config for Runtime { type AssetManager = AssetManager; - type DisputeBond = DisputeBond; - type DisputeFactor = DisputeFactor; + type OutcomeBond = OutcomeBond; + type OutcomeFactor = OutcomeFactor; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; #[cfg(feature = "with-global-disputes")] diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 060e26a5b..acda8068d 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -149,11 +149,9 @@ parameter_types! { pub const AdvisoryBond: Balance = 200 * BASE; pub const AdvisoryBondSlashPercentage: Percent = Percent::from_percent(0); /// (Slashable) Bond that is provided for disputing the outcome. - /// Slashed in case the final outcome does not match the dispute for which the `DisputeBond` - /// was deposited. - pub const DisputeBond: Balance = 2_000 * BASE; - /// `DisputeBond` is increased by this factor after every dispute. - pub const DisputeFactor: Balance = 2 * BASE; + /// Unreserved in case the dispute was justified otherwise slashed. + /// This is when the resolved outcome is different to the default (reported) outcome. + pub const DisputeBond: Balance = 5_000 * BASE; /// Maximum Categories a prediciton market can have (excluding base asset). pub const MaxCategories: u16 = MAX_CATEGORIES; /// Maximum number of disputes. @@ -217,6 +215,12 @@ parameter_types! { // Simple disputes parameters /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. pub const SimpleDisputesPalletId: PalletId = SD_PALLET_ID; + /// (Slashable) Bond that is provided for overriding the last outcome addition. + /// Slashed in case the final outcome does not match the dispute for which the `OutcomeBond` + /// was deposited. + pub const OutcomeBond: Balance = 2_000 * BASE; + /// `OutcomeBond` is increased by this factor after every new outcome addition. + pub const OutcomeFactor: Balance = 2 * BASE; // Swaps parameters /// A precentage from the withdrawal amount a liquidity provider wants to withdraw diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index c667a7fb9..d67b3790e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -103,10 +103,7 @@ mod pallet { // TODO take a bond from the caller ensure_signed(origin)?; let market = T::MarketCommons::market(&market_id)?; - ensure!( - market.status == MarketStatus::Disputed, - Error::::MarketIsNotDisputed - ); + ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism @@ -427,7 +424,9 @@ mod pallet { // // Result is capped to `usize::MAX` or in other words, capped to a very, very, very // high number of jurors. - fn necessary_jurors_num(disputes: &[MarketDispute]) -> usize { + fn necessary_jurors_num( + disputes: &[MarketDispute>], + ) -> usize { let len = disputes.len(); INITIAL_JURORS_NUM.saturating_add(SUBSEQUENT_JURORS_FACTOR.saturating_mul(len)) } @@ -545,7 +544,7 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 5d74739ae..589434e98 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -52,7 +52,7 @@ const DEFAULT_MARKET: MarketOf = Market { resolved_outcome: None, status: MarketStatus::Disputed, scoring_rule: ScoringRule::CPMM, - bonds: MarketBonds { creation: None, oracle: None }, + bonds: MarketBonds { creation: None, oracle: None, dispute: None }, }; const DEFAULT_SET_OF_JURORS: &[(u128, Juror)] = &[ (7, Juror { status: JurorStatus::Ok }), diff --git a/zrml/market-commons/src/tests.rs b/zrml/market-commons/src/tests.rs index f54664819..02bd5d030 100644 --- a/zrml/market-commons/src/tests.rs +++ b/zrml/market-commons/src/tests.rs @@ -45,7 +45,7 @@ const MARKET_DUMMY: Market = Market resolved_outcome: None, scoring_rule: ScoringRule::CPMM, status: MarketStatus::Disputed, - bonds: MarketBonds { creation: None, oracle: None }, + bonds: MarketBonds { creation: None, oracle: None, dispute: None }, }; #[test] diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 52c0c6cf5..d479cfab0 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -58,9 +58,9 @@ mod pallet { constants::MILLISECS_PER_BLOCK, traits::{DisputeApi, DisputeResolutionApi, Swaps, ZeitgeistAssetManager}, types::{ - Asset, Bond, Deadlines, Market, MarketBonds, MarketCreation, MarketDispute, - MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, MultiHash, - OutcomeReport, Report, ScalarPosition, ScoringRule, SubsidyUntil, + Asset, Bond, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, MultiHash, OldMarketDispute, OutcomeReport, + Report, ScalarPosition, ScoringRule, SubsidyUntil, }, }; #[cfg(feature = "with-global-disputes")] @@ -520,7 +520,7 @@ mod pallet { origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, ) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; + let who = ensure_signed(origin)?; let market = >::market(&market_id)?; ensure!(market.status == MarketStatus::Reported, Error::::InvalidMarketStatus); @@ -536,7 +536,15 @@ mod pallet { } } - Self::set_market_as_disputed(&market, &market_id)?; + let dispute_bond = T::DisputeBond::get(); + + T::AssetManager::reserve_named(&Self::reserve_id(), Asset::Ztg, &who, dispute_bond)?; + + >::mutate_market(&market_id, |m| { + m.status = MarketStatus::Disputed; + m.bonds.dispute = Some(Bond::new(who.clone(), dispute_bond)); + Ok(()) + })?; Self::deposit_event(Event::MarketDisputed(market_id, MarketStatus::Disputed)); @@ -650,10 +658,12 @@ mod pallet { MarketCreation::Advised => MarketBonds { creation: Some(Bond::new(sender.clone(), T::AdvisoryBond::get())), oracle: Some(Bond::new(sender.clone(), T::OracleBond::get())), + ..Default::default() }, MarketCreation::Permissionless => MarketBonds { creation: Some(Bond::new(sender.clone(), T::ValidityBond::get())), oracle: Some(Bond::new(sender.clone(), T::OracleBond::get())), + ..Default::default() }, }; @@ -1399,6 +1409,10 @@ mod pallet { /// The origin that is allowed to destroy markets. type DestroyOrigin: EnsureOrigin; + /// The base amount of currency that must be bonded in order to create a dispute. + #[pallet::constant] + type DisputeBond: Get>; + /// Event type Event: From> + IsType<::Event>; @@ -1793,7 +1807,7 @@ mod pallet { _, Blake2_128Concat, MarketIdOf, - BoundedVec, T::MaxDisputes>, + BoundedVec, T::MaxDisputes>, ValueQuery, >; @@ -1879,8 +1893,10 @@ mod pallet { impl Pallet { impl_unreserve_bond!(unreserve_creation_bond, creation); impl_unreserve_bond!(unreserve_oracle_bond, oracle); + impl_unreserve_bond!(unreserve_dispute_bond, dispute); impl_slash_bond!(slash_creation_bond, creation); impl_slash_bond!(slash_oracle_bond, oracle); + impl_slash_bond!(slash_dispute_bond, dispute); pub fn outcome_assets( market_id: MarketIdOf, @@ -2340,6 +2356,15 @@ mod pallet { // overall_imbalance.subsume(imbalance); } + if report.outcome != resolved_outcome { + // If the report outcome was wrong, the dispute was justified + Self::unreserve_dispute_bond(market_id)?; + } else { + // TODO what should be done with the DisputeBond imbalance? + let _imbalance = Self::slash_dispute_bond(market_id, None)?; + // overall_imbalance.subsume(imbalance); + } + Ok(resolved_outcome) } @@ -2621,20 +2646,6 @@ mod pallet { )) } - // If the market is already disputed, does nothing. - fn set_market_as_disputed( - market: &MarketOf, - market_id: &MarketIdOf, - ) -> DispatchResult { - if market.status != MarketStatus::Disputed { - >::mutate_market(market_id, |m| { - m.status = MarketStatus::Disputed; - Ok(()) - })?; - } - Ok(()) - } - // If a market has a pool that is `Active`, then changes from `Active` to `Clean`. If // the market does not exist or the market does not have a pool, does nothing. fn clean_up_pool( diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index 618ebcf94..49aee5ec3 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -123,7 +123,7 @@ impl OnRuntimeUpgrade for RecordBonds::OracleBond::get(), is_settled: false, }), + dispute: None, }, ), construct_markets( @@ -334,6 +335,7 @@ mod tests { value: ::OracleBond::get(), is_settled: true, }), + dispute: None, }, ), construct_markets( @@ -350,6 +352,7 @@ mod tests { value: ::OracleBond::get(), is_settled: false, }), + dispute: None, }, ), construct_markets( @@ -366,6 +369,7 @@ mod tests { value: ::OracleBond::get(), is_settled: false, }), + dispute: None, }, ), construct_markets( @@ -382,6 +386,7 @@ mod tests { value: ::OracleBond::get(), is_settled: true, }), + dispute: None, }, ), // Technically, the market below has the wrong scoring rule, but that's irrelevant to @@ -400,6 +405,7 @@ mod tests { value: ::OracleBond::get(), is_settled: true, }), + dispute: None, }, ), ] diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 4476b3eb8..516a9ee5c 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -36,13 +36,13 @@ use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; use zeitgeist_primitives::{ constants::mock::{ AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, CorrectionPeriod, - CourtCaseDuration, CourtPalletId, DisputeFactor, ExistentialDeposit, ExistentialDeposits, - ExitFee, GetNativeCurrencyId, LiquidityMiningPalletId, MaxApprovals, MaxAssets, - MaxCategories, MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGracePeriod, - MaxInRatio, MaxMarketPeriod, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, - MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, - MinCategories, MinDisputeDuration, MinLiquidity, MinOracleDuration, MinSubsidy, - MinSubsidyPeriod, MinWeight, MinimumPeriod, PmPalletId, SimpleDisputesPalletId, + CourtCaseDuration, CourtPalletId, ExistentialDeposit, ExistentialDeposits, ExitFee, + GetNativeCurrencyId, LiquidityMiningPalletId, MaxApprovals, MaxAssets, MaxCategories, + MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, + MaxMarketPeriod, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, + MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, + MinDisputeDuration, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, + MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, PmPalletId, SimpleDisputesPalletId, StakeWeight, SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ @@ -142,6 +142,7 @@ impl crate::Config for Runtime { type CloseOrigin = EnsureSignedBy; type Court = Court; type DestroyOrigin = EnsureSignedBy; + type DisputeBond = DisputeBond; type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; @@ -305,8 +306,8 @@ impl zrml_rikiddo::Config for Runtime { impl zrml_simple_disputes::Config for Runtime { type AssetManager = AssetManager; type Event = Event; - type DisputeBond = DisputeBond; - type DisputeFactor = DisputeFactor; + type OutcomeBond = OutcomeBond; + type OutcomeFactor = OutcomeFactor; type DisputeResolution = prediction_markets::Pallet; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; @@ -458,7 +459,7 @@ mod tests { ); assert_ne!( ::AdvisoryBond::get(), - ::DisputeBond::get() + ::DisputeBond::get() ); assert_ne!( ::OracleBond::get(), @@ -466,11 +467,11 @@ mod tests { ); assert_ne!( ::OracleBond::get(), - ::DisputeBond::get() + ::DisputeBond::get() ); assert_ne!( ::ValidityBond::get(), - ::DisputeBond::get() + ::DisputeBond::get() ); } } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index dff638bbc..b62e6cef4 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -34,7 +34,7 @@ use test_case::test_case; use orml_traits::MultiCurrency; use sp_runtime::traits::{AccountIdConversion, SaturatedConversion, Zero}; use zeitgeist_primitives::{ - constants::mock::{DisputeFactor, BASE, CENT, MILLISECS_PER_BLOCK}, + constants::mock::{OutcomeFactor, BASE, CENT, MILLISECS_PER_BLOCK}, traits::Swaps as SwapsPalletApi, types::{ AccountIdTest, Asset, Balance, BlockNumber, Bond, Deadlines, Market, MarketBonds, @@ -3941,6 +3941,7 @@ fn report_fails_if_reporter_is_not_the_oracle() { MarketBonds { creation: Some(Bond::new(ALICE, ::AdvisoryBond::get())), oracle: Some(Bond::new(ALICE, ::OracleBond::get())), + dispute: None, } )] #[test_case( @@ -3950,6 +3951,7 @@ fn report_fails_if_reporter_is_not_the_oracle() { MarketBonds { creation: Some(Bond::new(ALICE, ::ValidityBond::get())), oracle: Some(Bond::new(ALICE, ::OracleBond::get())), + dispute: None, } )] fn create_market_sets_the_correct_market_parameters_and_reserves_the_correct_amount( diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index b49cbc4d9..bbd7b9b55 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -74,7 +74,7 @@ mod pallet { /// The base amount of currency that must be bonded in order to create a dispute. #[pallet::constant] - type DisputeBond: Get>; + type OutcomeBond: Get>; type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, @@ -86,7 +86,7 @@ mod pallet { /// The additional amount of currency that must be bonded when creating a subsequent /// dispute. #[pallet::constant] - type DisputeFactor: Get>; + type OutcomeFactor: Get>; /// See [`GlobalDisputesPalletApi`]. #[cfg(feature = "with-global-disputes")] @@ -145,7 +145,7 @@ mod pallet { _, Blake2_128Concat, MarketIdOf, - BoundedVec, T::MaxDisputes>, + BoundedVec>, T::MaxDisputes>, ValueQuery, >; @@ -157,7 +157,7 @@ mod pallet { { OutcomeReserved { market_id: MarketIdOf, - dispute: MarketDispute, + dispute: MarketDispute>, }, } @@ -204,14 +204,11 @@ mod pallet { Self::ensure_can_not_dispute_the_same_outcome(&disputes, report, &outcome)?; Self::ensure_disputes_does_not_exceed_max_disputes(num_disputes)?; - T::AssetManager::reserve_named( - &Self::reserve_id(), - Asset::Ztg, - &who, - default_dispute_bond::(disputes.len()), - )?; + let bond = default_outcome_bond::(disputes.len()); - let market_dispute = MarketDispute { at: now, by: who, outcome }; + T::AssetManager::reserve_named(&Self::reserve_id(), Asset::Ztg, &who, bond)?; + + let market_dispute = MarketDispute { at: now, by: who, outcome, bond }; >::try_mutate(market_id, |disputes| { disputes.try_push(market_dispute.clone()).map_err(|_| >::StorageOverflow) })?; @@ -235,7 +232,7 @@ mod pallet { } fn ensure_can_not_dispute_the_same_outcome( - disputes: &[MarketDispute], + disputes: &[MarketDispute>], report: &Report, outcome: &OutcomeReport, ) -> DispatchResult { @@ -255,7 +252,7 @@ mod pallet { } fn get_auto_resolve( - disputes: &[MarketDispute], + disputes: &[MarketDispute>], market: &MarketOf, ) -> Option { disputes.last().map(|last_dispute| { @@ -264,7 +261,7 @@ mod pallet { } fn remove_auto_resolve( - disputes: &[MarketDispute], + disputes: &[MarketDispute>], market_id: &MarketIdOf, market: &MarketOf, ) { @@ -320,14 +317,13 @@ mod pallet { let mut overall_imbalance = NegativeImbalanceOf::::zero(); - for (i, dispute) in disputes.iter().enumerate() { - let actual_bond = default_dispute_bond::(i); + for dispute in disputes.iter() { if dispute.outcome == resolved_outcome { T::AssetManager::unreserve_named( &Self::reserve_id(), Asset::Ztg, &dispute.by, - actual_bond, + dispute.bond.saturated_into::().saturated_into(), ); correct_reporters.push(dispute.by.clone()); @@ -335,7 +331,7 @@ mod pallet { let (imbalance, _) = CurrencyOf::::slash_reserved_named( &Self::reserve_id(), &dispute.by, - actual_bond.saturated_into::().saturated_into(), + dispute.bond.saturated_into::().saturated_into(), ); overall_imbalance.subsume(imbalance); } @@ -406,14 +402,8 @@ mod pallet { )?; } - for (index, MarketDispute { at: _, by, outcome }) in disputes.iter().enumerate() { - let dispute_bond = default_dispute_bond::(index); - T::GlobalDisputes::push_voting_outcome( - market_id, - outcome.clone(), - by, - dispute_bond, - )?; + for MarketDispute { at: _, by, outcome, bond } in disputes.iter() { + T::GlobalDisputes::push_voting_outcome(market_id, outcome.clone(), by, bond)?; } } Ok(()) @@ -427,12 +417,12 @@ mod pallet { // `Disputes` is emtpy unless the market is disputed, so this is just a defensive // check. if market.status == MarketStatus::Disputed { - for (index, dispute) in Disputes::::take(market_id).iter().enumerate() { + for dispute in Disputes::::take(market_id).iter() { T::AssetManager::unreserve_named( &Self::reserve_id(), Asset::Ztg, &dispute.by, - default_dispute_bond::(index), + dispute.bond.saturated_into::().saturated_into(), ); } } @@ -443,12 +433,12 @@ mod pallet { impl SimpleDisputesPalletApi for Pallet where T: Config {} // No-one can bound more than BalanceOf, therefore, this functions saturates - pub fn default_dispute_bond(n: usize) -> BalanceOf + pub fn default_outcome_bond(n: usize) -> BalanceOf where T: Config, { - T::DisputeBond::get().saturating_add( - T::DisputeFactor::get().saturating_mul(n.saturated_into::().into()), + T::OutcomeBond::get().saturating_add( + T::OutcomeFactor::get().saturating_mul(n.saturated_into::().into()), ) } } diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 59ba06cb3..971039f95 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -30,7 +30,7 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, DisputeBond, DisputeFactor, ExistentialDeposits, GetNativeCurrencyId, + BlockHashCount, OutcomeBond, OutcomeFactor, ExistentialDeposits, GetNativeCurrencyId, MaxApprovals, MaxDisputes, MaxReserves, MinimumPeriod, PmPalletId, SimpleDisputesPalletId, TreasuryPalletId, }, @@ -129,8 +129,8 @@ impl DisputeResolutionApi for NoopResolution { impl crate::Config for Runtime { type AssetManager = AssetManager; type Event = (); - type DisputeBond = DisputeBond; - type DisputeFactor = DisputeFactor; + type OutcomeBond = OutcomeBond; + type OutcomeFactor = OutcomeFactor; type DisputeResolution = NoopResolution; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index a62d21549..c479f84ee 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -23,6 +23,7 @@ use crate::{ }; use frame_support::{assert_noop, BoundedVec}; use zeitgeist_primitives::{ + constants::mock::{OutcomeBond, OutcomeFactor}, traits::DisputeApi, types::{ Deadlines, Market, MarketBonds, MarketCreation, MarketDispute, MarketDisputeMechanism, @@ -44,7 +45,7 @@ const DEFAULT_MARKET: MarketOf = Market { resolved_outcome: None, scoring_rule: ScoringRule::CPMM, status: MarketStatus::Disputed, - bonds: MarketBonds { creation: None, oracle: None }, + bonds: MarketBonds { creation: None, oracle: None, dispute: None }, }; #[test] @@ -78,8 +79,18 @@ fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outc market.status = MarketStatus::Disputed; let disputes = BoundedVec::try_from( [ - MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(0) }, - MarketDispute { at: 0, by: 0, outcome: OutcomeReport::Scalar(20) }, + MarketDispute { + at: 0, + by: 0, + outcome: OutcomeReport::Scalar(0), + bond: OutcomeBond::get(), + }, + MarketDispute { + at: 0, + by: 0, + outcome: OutcomeReport::Scalar(20), + bond: OutcomeFactor::get() * OutcomeBond::get(), + }, ] .to_vec(), ) From 9448ab9cf29902028faaa2a35260ad233db66e9f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 16 Jan 2023 18:30:05 +0100 Subject: [PATCH 025/368] mdm funds flow --- primitives/src/traits/dispute_api.rs | 15 ++++++- runtime/common/src/lib.rs | 1 - runtime/zeitgeist/src/parameters.rs | 2 +- zrml/authorized/src/lib.rs | 19 +++++++- zrml/authorized/src/tests.rs | 15 ++++--- zrml/court/src/lib.rs | 19 +++++++- zrml/court/src/tests.rs | 28 ++++++------ zrml/prediction-markets/src/lib.rs | 67 +++++++++++++++++++++++----- zrml/prediction-markets/src/mock.rs | 6 +-- zrml/prediction-markets/src/tests.rs | 40 +++++++++-------- zrml/simple-disputes/src/lib.rs | 33 +++++++++----- zrml/simple-disputes/src/mock.rs | 5 +-- zrml/simple-disputes/src/tests.rs | 8 ++-- 13 files changed, 179 insertions(+), 79 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index d415be8d3..b7e576cd5 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -31,6 +31,7 @@ type MarketOfDisputeApi = Market< pub trait DisputeApi { type AccountId; type Balance; + type NegativeImbalance; type BlockNumber; type MarketId; type Moment; @@ -51,11 +52,23 @@ pub trait DisputeApi { /// /// Returns the dispute mechanism's report if available, otherwise `None`. If `None` is /// returned, this means that the dispute could not be resolved. - fn on_resolution( + fn get_resolution_outcome( market_id: &Self::MarketId, market: &MarketOfDisputeApi, ) -> Result, DispatchError>; + /// Allow the flow of funds to the market dispute mechanism. + /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. + /// + /// # Returns + /// Returns the negative imbalance which is meant to be used for the treasury. + fn maybe_pay( + market_id: &Self::MarketId, + market: &MarketOfDisputeApi, + resolved_outcome: &OutcomeReport, + overall_imbalance: Self::NegativeImbalance, + ) -> Result; + /// Query the future resolution block of a disputed market. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. /// diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index d52f9189d..2d308a866 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1052,7 +1052,6 @@ macro_rules! impl_config_traits { type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; type PredictionMarketsPalletId = PmPalletId; - type Slash = Treasury; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index acda8068d..3aa2a2cc2 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -151,7 +151,7 @@ parameter_types! { /// (Slashable) Bond that is provided for disputing the outcome. /// Unreserved in case the dispute was justified otherwise slashed. /// This is when the resolved outcome is different to the default (reported) outcome. - pub const DisputeBond: Balance = 5_000 * BASE; + pub const DisputeBond: Balance = 2_000 * BASE; /// Maximum Categories a prediciton market can have (excluding base asset). pub const MaxCategories: u16 = MAX_CATEGORIES; /// Maximum number of disputes. diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 35e742264..00bcfd7c0 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -57,6 +57,8 @@ mod pallet { as Currency<::AccountId>>::Balance; pub(crate) type CurrencyOf = <::MarketCommons as MarketCommonsPalletApi>::Currency; + pub(crate) type NegativeImbalanceOf = + as Currency<::AccountId>>::NegativeImbalance; pub(crate) type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; @@ -195,6 +197,7 @@ mod pallet { { type AccountId = T::AccountId; type Balance = BalanceOf; + type NegativeImbalance = NegativeImbalanceOf; type BlockNumber = T::BlockNumber; type MarketId = MarketIdOf; type Moment = MomentOf; @@ -208,7 +211,7 @@ mod pallet { Ok(()) } - fn on_resolution( + fn get_resolution_outcome( market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -220,6 +223,20 @@ mod pallet { Ok(report.map(|r| r.outcome)) } + fn maybe_pay( + _: &Self::MarketId, + market: &MarketOf, + _: &OutcomeReport, + overall_imbalance: NegativeImbalanceOf, + ) -> Result, DispatchError> { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Authorized, + Error::::MarketDoesNotHaveDisputeMechanismAuthorized + ); + // all funds to treasury + Ok(overall_imbalance) + } + fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, diff --git a/zrml/authorized/src/tests.rs b/zrml/authorized/src/tests.rs index 4c5550000..a49095014 100644 --- a/zrml/authorized/src/tests.rs +++ b/zrml/authorized/src/tests.rs @@ -157,15 +157,15 @@ fn authorize_market_outcome_fails_on_unauthorized_account() { } #[test] -fn on_resolution_fails_if_no_report_was_submitted() { +fn get_resolution_outcome_fails_if_no_report_was_submitted() { ExtBuilder::default().build().execute_with(|| { - let report = Authorized::on_resolution(&0, &market_mock::()).unwrap(); + let report = Authorized::get_resolution_outcome(&0, &market_mock::()).unwrap(); assert!(report.is_none()); }); } #[test] -fn on_resolution_removes_stored_outcomes() { +fn get_resolution_outcome_removes_stored_outcomes() { ExtBuilder::default().build().execute_with(|| { let market = market_mock::(); Markets::::insert(0, &market); @@ -174,13 +174,13 @@ fn on_resolution_removes_stored_outcomes() { 0, OutcomeReport::Scalar(2) )); - assert_ok!(Authorized::on_resolution(&0, &market)); + assert_ok!(Authorized::get_resolution_outcome(&0, &market)); assert_eq!(AuthorizedOutcomeReports::::get(0), None); }); } #[test] -fn on_resolution_returns_the_reported_outcome() { +fn get_resolution_outcome_returns_the_reported_outcome() { ExtBuilder::default().build().execute_with(|| { let market = market_mock::(); Markets::::insert(0, &market); @@ -195,7 +195,10 @@ fn on_resolution_returns_the_reported_outcome() { 0, OutcomeReport::Scalar(2) )); - assert_eq!(Authorized::on_resolution(&0, &market).unwrap(), Some(OutcomeReport::Scalar(2))); + assert_eq!( + Authorized::get_resolution_outcome(&0, &market).unwrap(), + Some(OutcomeReport::Scalar(2)) + ); }); } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d67b3790e..1491481a4 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -86,6 +86,8 @@ mod pallet { as Currency<::AccountId>>::Balance; pub(crate) type CurrencyOf = <::MarketCommons as MarketCommonsPalletApi>::Currency; + pub(crate) type NegativeImbalanceOf = + as Currency<::AccountId>>::NegativeImbalance; pub(crate) type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; @@ -539,6 +541,7 @@ mod pallet { { type AccountId = T::AccountId; type Balance = BalanceOf; + type NegativeImbalance = NegativeImbalanceOf; type BlockNumber = T::BlockNumber; type MarketId = MarketIdOf; type Moment = MomentOf; @@ -557,7 +560,7 @@ mod pallet { // rewarded if sided on the most voted outcome but jurors that voted second most // voted outcome (winner of the losing majority) are placed as tardy instead of // being slashed. - fn on_resolution( + fn get_resolution_outcome( market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -585,6 +588,20 @@ mod pallet { Ok(Some(first)) } + fn maybe_pay( + _: &Self::MarketId, + market: &MarketOf, + _: &OutcomeReport, + overall_imbalance: NegativeImbalanceOf, + ) -> Result, DispatchError> { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + Error::::MarketDoesNotHaveCourtMechanism + ); + // TODO all funds to treasury? + Ok(overall_imbalance) + } + fn get_auto_resolve( _: &Self::MarketId, market: &MarketOf, diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 589434e98..dcdfac4e0 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -138,12 +138,12 @@ fn on_dispute_denies_non_court_markets() { } #[test] -fn on_resolution_denies_non_court_markets() { +fn get_resolution_outcome_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; assert_noop!( - Court::on_resolution(&0, &market), + Court::get_resolution_outcome(&0, &market), Error::::MarketDoesNotHaveCourtMechanism ); }); @@ -167,7 +167,7 @@ fn appeal_stores_jurors_that_should_vote() { // Alice is the winner, Bob is tardy and Charlie is the loser #[test] -fn on_resolution_awards_winners_and_slashes_losers() { +fn get_resolution_outcome_awards_winners_and_slashes_losers() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -178,7 +178,7 @@ fn on_resolution_awards_winners_and_slashes_losers() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(2)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(3)).unwrap(); - let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Balances::free_balance(ALICE), 998 * BASE + 3 * BASE); assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), 2 * BASE); assert_eq!(Balances::free_balance(BOB), 996 * BASE); @@ -189,7 +189,7 @@ fn on_resolution_awards_winners_and_slashes_losers() { } #[test] -fn on_resolution_decides_market_outcome_based_on_the_majority() { +fn get_resolution_outcome_decides_market_outcome_based_on_the_majority() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -200,13 +200,13 @@ fn on_resolution_decides_market_outcome_based_on_the_majority() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let outcome = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); + let outcome = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(outcome, Some(OutcomeReport::Scalar(1))); }); } #[test] -fn on_resolution_sets_late_jurors_as_tardy() { +fn get_resolution_outcome_sets_late_jurors_as_tardy() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -214,14 +214,14 @@ fn on_resolution_sets_late_jurors_as_tardy() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); - let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(ALICE).unwrap().status, JurorStatus::Ok); assert_eq!(Jurors::::get(BOB).unwrap().status, JurorStatus::Tardy); }); } #[test] -fn on_resolution_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tardy() { +fn get_resolution_outcome_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tardy() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -232,13 +232,13 @@ fn on_resolution_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tard Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(CHARLIE).unwrap().status, JurorStatus::Tardy); }); } #[test] -fn on_resolution_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { +fn get_resolution_outcome_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -247,7 +247,7 @@ fn on_resolution_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); - let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); let join_court_stake = 40000000000; let slash = join_court_stake / 5; assert_eq!(Balances::free_balance(Court::treasury_account_id()), INITIAL_BALANCE + slash); @@ -257,7 +257,7 @@ fn on_resolution_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { } #[test] -fn on_resolution_removes_requested_jurors_and_votes() { +fn get_resolution_outcome_removes_requested_jurors_and_votes() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -268,7 +268,7 @@ fn on_resolution_removes_requested_jurors_and_votes() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(RequestedJurors::::iter().count(), 0); assert_eq!(Votes::::iter().count(), 0); }); diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index d479cfab0..5285e2f0c 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -238,6 +238,11 @@ mod pallet { Self::slash_oracle_bond(&market_id, None)?; } } + if let Some(bond) = market.bonds.dispute { + if !bond.is_settled { + Self::unreserve_dispute_bond(&market_id)?; + } + } if market_status == MarketStatus::Proposed { MarketIdsForEdit::::remove(market_id); @@ -1387,6 +1392,7 @@ mod pallet { type Authorized: zrml_authorized::AuthorizedPalletApi< AccountId = Self::AccountId, Balance = BalanceOf, + NegativeImbalance = NegativeImbalanceOf, BlockNumber = Self::BlockNumber, MarketId = MarketIdOf, Moment = MomentOf, @@ -1400,6 +1406,7 @@ mod pallet { type Court: zrml_court::CourtPalletApi< AccountId = Self::AccountId, Balance = BalanceOf, + NegativeImbalance = NegativeImbalanceOf, BlockNumber = Self::BlockNumber, MarketId = MarketIdOf, Moment = MomentOf, @@ -1513,6 +1520,7 @@ mod pallet { type SimpleDisputes: DisputeApi< AccountId = Self::AccountId, Balance = BalanceOf, + NegativeImbalance = NegativeImbalanceOf, BlockNumber = Self::BlockNumber, MarketId = MarketIdOf, Moment = MomentOf, @@ -2334,11 +2342,13 @@ mod pallet { if resolved_outcome_option.is_none() { resolved_outcome_option = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::on_resolution(market_id, market)? + T::Authorized::get_resolution_outcome(market_id, market)? + } + MarketDisputeMechanism::Court => { + T::Court::get_resolution_outcome(market_id, market)? } - MarketDisputeMechanism::Court => T::Court::on_resolution(market_id, market)?, MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_resolution(market_id, market)? + T::SimpleDisputes::get_resolution_outcome(market_id, market)? } }; } @@ -2346,25 +2356,58 @@ mod pallet { let resolved_outcome = resolved_outcome_option.unwrap_or_else(|| report.outcome.clone()); + let mut overall_imbalance = >::zero(); + // If the oracle reported right, return the OracleBond, otherwise slash it to // pay the correct reporters. if report.by == market.oracle && report.outcome == resolved_outcome { Self::unreserve_oracle_bond(market_id)?; } else { - let _imbalance = Self::slash_oracle_bond(market_id, None)?; - // TODO what should be done with the OracleBond imbalance? - // overall_imbalance.subsume(imbalance); + let imb = Self::slash_oracle_bond(market_id, None)?; + overall_imbalance.subsume(imb); + } + + let mut correct_disputor = None; + if let Some(bond) = market.bonds.dispute.clone() { + if !bond.is_settled { + if report.outcome != resolved_outcome { + // If the report outcome was wrong, the dispute was justified + Self::unreserve_dispute_bond(market_id)?; + correct_disputor = Some(bond.who); + } else { + let imb = Self::slash_dispute_bond(market_id, None)?; + overall_imbalance.subsume(imb); + } + } } - if report.outcome != resolved_outcome { - // If the report outcome was wrong, the dispute was justified - Self::unreserve_dispute_bond(market_id)?; + let mut imbalance_left = >::zero(); + if let Some(disputor) = correct_disputor { + CurrencyOf::::resolve_creating(&disputor, overall_imbalance); } else { - // TODO what should be done with the DisputeBond imbalance? - let _imbalance = Self::slash_dispute_bond(market_id, None)?; - // overall_imbalance.subsume(imbalance); + imbalance_left = overall_imbalance; } + let remainder = match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => T::Authorized::maybe_pay( + market_id, + market, + &resolved_outcome, + imbalance_left, + )?, + MarketDisputeMechanism::Court => { + T::Court::maybe_pay(market_id, market, &resolved_outcome, imbalance_left)? + } + MarketDisputeMechanism::SimpleDisputes => T::SimpleDisputes::maybe_pay( + market_id, + market, + &resolved_outcome, + imbalance_left, + )?, + }; + + T::Slash::on_unbalanced(remainder); + Ok(resolved_outcome) } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 516a9ee5c..a209d8b49 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -315,7 +315,6 @@ impl zrml_simple_disputes::Config for Runtime { type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; type PredictionMarketsPalletId = PmPalletId; - type Slash = Treasury; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } @@ -465,10 +464,7 @@ mod tests { ::OracleBond::get(), ::ValidityBond::get() ); - assert_ne!( - ::OracleBond::get(), - ::DisputeBond::get() - ); + assert_ne!(::OracleBond::get(), ::DisputeBond::get()); assert_ne!( ::ValidityBond::get(), ::DisputeBond::get() diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index b62e6cef4..5e11adaf5 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -19,9 +19,8 @@ #![allow(clippy::reversed_empty_ranges)] use crate::{ - mock::*, Config, Disputes, Error, Event, LastTimeFrame, MarketIdsForEdit, - MarketIdsPerCloseBlock, MarketIdsPerDisputeBlock, MarketIdsPerOpenBlock, - MarketIdsPerReportBlock, + mock::*, Config, Error, Event, LastTimeFrame, MarketIdsForEdit, MarketIdsPerCloseBlock, + MarketIdsPerDisputeBlock, MarketIdsPerOpenBlock, MarketIdsPerReportBlock, }; use core::ops::{Range, RangeInclusive}; use frame_support::{ @@ -34,7 +33,7 @@ use test_case::test_case; use orml_traits::MultiCurrency; use sp_runtime::traits::{AccountIdConversion, SaturatedConversion, Zero}; use zeitgeist_primitives::{ - constants::mock::{OutcomeFactor, BASE, CENT, MILLISECS_PER_BLOCK}, + constants::mock::{OutcomeBond, OutcomeFactor, BASE, CENT, MILLISECS_PER_BLOCK}, traits::Swaps as SwapsPalletApi, types::{ AccountIdTest, Asset, Balance, BlockNumber, Bond, Deadlines, Market, MarketBonds, @@ -475,13 +474,15 @@ fn admin_destroy_market_correctly_unreserves_dispute_bonds() { ); assert_eq!( Balances::free_balance(CHARLIE), - balance_free_before_charlie + zrml_simple_disputes::default_dispute_bond::(0) + balance_free_before_charlie + + DisputeBond::get() + + zrml_simple_disputes::default_outcome_bond::(0) ); assert_eq!( Balances::free_balance(DAVE), - balance_free_before_dave + zrml_simple_disputes::default_dispute_bond::(1), + balance_free_before_dave + zrml_simple_disputes::default_outcome_bond::(1), ); - assert!(Disputes::::get(market_id).is_empty()); + assert!(zrml_simple_disputes::Disputes::::get(market_id).is_empty()); }); } @@ -2214,6 +2215,12 @@ fn it_resolves_a_disputed_market() { assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + let market = MarketCommons::market(&0).unwrap(); + assert_eq!(market.status, MarketStatus::Disputed); + + let charlie_reserved = Balances::reserved_balance(&CHARLIE); + assert_eq!(charlie_reserved, DisputeBond::get()); + let dispute_at_0 = report_at + 1; run_to_block(dispute_at_0); @@ -2246,13 +2253,13 @@ fn it_resolves_a_disputed_market() { // check everyone's deposits let charlie_reserved = Balances::reserved_balance(&CHARLIE); - assert_eq!(charlie_reserved, DisputeBond::get()); + assert_eq!(charlie_reserved, DisputeBond::get() + OutcomeBond::get()); let dave_reserved = Balances::reserved_balance(&DAVE); - assert_eq!(dave_reserved, DisputeBond::get() + DisputeFactor::get()); + assert_eq!(dave_reserved, OutcomeBond::get() + OutcomeFactor::get()); let eve_reserved = Balances::reserved_balance(&EVE); - assert_eq!(eve_reserved, DisputeBond::get() + 2 * DisputeFactor::get()); + assert_eq!(eve_reserved, OutcomeBond::get() + 2 * OutcomeFactor::get()); // check disputes length let disputes = zrml_simple_disputes::Disputes::::get(0); @@ -2286,12 +2293,12 @@ fn it_resolves_a_disputed_market() { // Make sure rewards are right: // // Slashed amounts: - // - Dave's reserve: DisputeBond::get() + DisputeFactor::get() + // - Dave's reserve: OutcomeBond::get() + OutcomeFactor::get() // - Alice's oracle bond: OracleBond::get() - // Total: OracleBond::get() + DisputeBond::get() + DisputeFactor::get() + // Total: OracleBond::get() + OutcomeBond::get() + OutcomeFactor::get() // // Charlie and Eve each receive half of the total slashed amount as bounty. - let dave_reserved = DisputeBond::get() + DisputeFactor::get(); + let dave_reserved = OutcomeBond::get() + OutcomeFactor::get(); let total_slashed = OracleBond::get() + dave_reserved; let charlie_balance = Balances::free_balance(&CHARLIE); @@ -2314,6 +2321,7 @@ fn it_resolves_a_disputed_market() { assert!(market_after.bonds.creation.unwrap().is_settled); assert!(market_after.bonds.oracle.unwrap().is_settled); + assert!(market_after.bonds.dispute.unwrap().is_settled); }); } @@ -3075,10 +3083,6 @@ fn authorized_correctly_resolves_disputed_market() { let charlie_reserved = Balances::reserved_balance(&CHARLIE); assert_eq!(charlie_reserved, DisputeBond::get()); - // check disputes length - let disputes = crate::Disputes::::get(0); - assert_eq!(disputes.len(), 1); - let market_ids_1 = MarketIdsPerDisputeBlock::::get( dispute_at + ::CorrectionPeriod::get(), ); @@ -3102,7 +3106,7 @@ fn authorized_correctly_resolves_disputed_market() { let market_after = MarketCommons::market(&0).unwrap(); assert_eq!(market_after.status, MarketStatus::Resolved); - let disputes = crate::Disputes::::get(0); + let disputes = zrml_simple_disputes::Disputes::::get(0); assert_eq!(disputes.len(), 0); assert_ok!(PredictionMarkets::redeem_shares(Origin::signed(CHARLIE), 0)); diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index bbd7b9b55..0bc3ec216 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -37,7 +37,7 @@ mod pallet { dispatch::DispatchResult, ensure, pallet_prelude::{Blake2_128Concat, DispatchResultWithPostInfo, StorageMap, ValueQuery}, - traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency, OnUnbalanced}, + traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency}, transactional, BoundedVec, PalletId, }; use frame_system::pallet_prelude::*; @@ -113,9 +113,6 @@ mod pallet { #[pallet::constant] type PredictionMarketsPalletId: Get; - /// Handler for slashed funds. - type Slash: OnUnbalanced>; - /// Weights generated by benchmarks type WeightInfo: WeightInfoZeitgeist; } @@ -280,6 +277,7 @@ mod pallet { { type AccountId = T::AccountId; type Balance = BalanceOf; + type NegativeImbalance = NegativeImbalanceOf; type BlockNumber = T::BlockNumber; type MarketId = MarketIdOf; type Moment = MomentOf; @@ -293,7 +291,7 @@ mod pallet { Ok(()) } - fn on_resolution( + fn get_resolution_outcome( market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -311,14 +309,27 @@ mod pallet { None => return Ok(None), }; - let resolved_outcome = last_dispute.outcome.clone(); + Ok(Some(last_dispute.outcome.clone())) + } - let mut correct_reporters: Vec = Vec::new(); + fn maybe_pay( + market_id: &Self::MarketId, + market: &MarketOf, + resolved_outcome: &OutcomeReport, + mut overall_imbalance: NegativeImbalanceOf, + ) -> Result, DispatchError> { + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, + Error::::MarketDoesNotHaveSimpleDisputesMechanism + ); + ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); - let mut overall_imbalance = NegativeImbalanceOf::::zero(); + let disputes = Disputes::::get(market_id); + + let mut correct_reporters: Vec = Vec::new(); for dispute in disputes.iter() { - if dispute.outcome == resolved_outcome { + if &dispute.outcome == resolved_outcome { T::AssetManager::unreserve_named( &Self::reserve_id(), Asset::Ztg, @@ -350,11 +361,9 @@ mod pallet { } } - T::Slash::on_unbalanced(overall_imbalance); - Disputes::::remove(market_id); - Ok(Some(resolved_outcome)) + Ok(overall_imbalance) } fn get_auto_resolve( diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 971039f95..e9872c558 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -30,8 +30,8 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, OutcomeBond, OutcomeFactor, ExistentialDeposits, GetNativeCurrencyId, - MaxApprovals, MaxDisputes, MaxReserves, MinimumPeriod, PmPalletId, SimpleDisputesPalletId, + BlockHashCount, ExistentialDeposits, GetNativeCurrencyId, MaxApprovals, MaxDisputes, + MaxReserves, MinimumPeriod, OutcomeBond, OutcomeFactor, PmPalletId, SimpleDisputesPalletId, TreasuryPalletId, }, traits::DisputeResolutionApi, @@ -138,7 +138,6 @@ impl crate::Config for Runtime { type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; type PredictionMarketsPalletId = PmPalletId; - type Slash = Treasury; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index c479f84ee..ff8a0170c 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -61,19 +61,19 @@ fn on_dispute_denies_non_simple_disputes_markets() { } #[test] -fn on_resolution_denies_non_simple_disputes_markets() { +fn get_resolution_outcome_denies_non_simple_disputes_markets() { ExtBuilder.build().execute_with(|| { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::Court; assert_noop!( - SimpleDisputes::on_resolution(&0, &market), + SimpleDisputes::get_resolution_outcome(&0, &market), Error::::MarketDoesNotHaveSimpleDisputesMechanism ); }); } #[test] -fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outcome() { +fn get_resolution_outcome_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outcome() { ExtBuilder.build().execute_with(|| { let mut market = DEFAULT_MARKET; market.status = MarketStatus::Disputed; @@ -97,7 +97,7 @@ fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outc .unwrap(); Disputes::::insert(0, &disputes); assert_eq!( - &SimpleDisputes::on_resolution(&0, &market).unwrap().unwrap(), + &SimpleDisputes::get_resolution_outcome(&0, &market).unwrap().unwrap(), &disputes.last().unwrap().outcome ) }); From c46621e0d9e3b6e1bb763c4d964e1b891e6f0177 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 17 Jan 2023 10:39:09 +0100 Subject: [PATCH 026/368] fix clippy --- zrml/court/src/tests.rs | 14 +++++++------- zrml/prediction-markets/src/lib.rs | 9 +++------ zrml/prediction-markets/src/tests.rs | 12 +++++++----- zrml/simple-disputes/src/lib.rs | 13 +++++-------- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index dcdfac4e0..2cc17b65d 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -155,7 +155,7 @@ fn appeal_stores_jurors_that_should_vote() { setup_blocks(123); let _ = Court::join_court(Origin::signed(ALICE)); let _ = Court::join_court(Origin::signed(BOB)); - MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); assert_noop!( Court::join_court(Origin::signed(ALICE)), @@ -173,7 +173,7 @@ fn get_resolution_outcome_awards_winners_and_slashes_losers() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(2)).unwrap(); @@ -195,7 +195,7 @@ fn get_resolution_outcome_decides_market_outcome_based_on_the_majority() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); @@ -212,7 +212,7 @@ fn get_resolution_outcome_sets_late_jurors_as_tardy() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(ALICE).unwrap().status, JurorStatus::Ok); @@ -227,7 +227,7 @@ fn get_resolution_outcome_sets_jurors_that_voted_on_the_second_most_voted_outcom Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); @@ -245,7 +245,7 @@ fn get_resolution_outcome_punishes_tardy_jurors_that_failed_to_vote_a_second_tim Court::join_court(Origin::signed(BOB)).unwrap(); Court::set_stored_juror_as_tardy(&BOB).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); let join_court_stake = 40000000000; @@ -263,7 +263,7 @@ fn get_resolution_outcome_removes_requested_jurors_and_votes() { Court::join_court(Origin::signed(ALICE)).unwrap(); Court::join_court(Origin::signed(BOB)).unwrap(); Court::join_court(Origin::signed(CHARLIE)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET.clone()).unwrap(); + MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 5285e2f0c..39fd9d0eb 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2389,12 +2389,9 @@ mod pallet { } let remainder = match market.dispute_mechanism { - MarketDisputeMechanism::Authorized => T::Authorized::maybe_pay( - market_id, - market, - &resolved_outcome, - imbalance_left, - )?, + MarketDisputeMechanism::Authorized => { + T::Authorized::maybe_pay(market_id, market, &resolved_outcome, imbalance_left)? + } MarketDisputeMechanism::Court => { T::Court::maybe_pay(market_id, market, &resolved_outcome, imbalance_left)? } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 5e11adaf5..24a690d4e 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -2295,14 +2295,16 @@ fn it_resolves_a_disputed_market() { // Slashed amounts: // - Dave's reserve: OutcomeBond::get() + OutcomeFactor::get() // - Alice's oracle bond: OracleBond::get() - // Total: OracleBond::get() + OutcomeBond::get() + OutcomeFactor::get() + // simple-disputes reward: OutcomeBond::get() + OutcomeFactor::get() + // Charlie gets OracleBond, because the dispute was justified. + // A dispute is justified if the oracle's report is different to the final outcome. // - // Charlie and Eve each receive half of the total slashed amount as bounty. + // Charlie and Eve each receive half of the simple-disputes reward as bounty. let dave_reserved = OutcomeBond::get() + OutcomeFactor::get(); - let total_slashed = OracleBond::get() + dave_reserved; + let total_slashed = dave_reserved; let charlie_balance = Balances::free_balance(&CHARLIE); - assert_eq!(charlie_balance, 1_000 * BASE + total_slashed / 2); + assert_eq!(charlie_balance, 1_000 * BASE + OracleBond::get() + total_slashed / 2); let charlie_reserved_2 = Balances::reserved_balance(&CHARLIE); assert_eq!(charlie_reserved_2, 0); let eve_balance = Balances::free_balance(&EVE); @@ -2415,7 +2417,7 @@ fn start_global_dispute_works() { ); for i in 1..=::MaxDisputes::get() { let dispute_bond = - zrml_simple_disputes::default_dispute_bond::((i - 1).into()); + zrml_simple_disputes::default_outcome_bond::((i - 1).into()); assert_eq!( GlobalDisputes::get_voting_outcome_info( &market_id, diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 0bc3ec216..5c8308654 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -117,7 +117,7 @@ mod pallet { type WeightInfo: WeightInfoZeitgeist; } - type BalanceOf = + pub(crate) type BalanceOf = as Currency<::AccountId>>::Balance; pub(crate) type CurrencyOf = <::MarketCommons as MarketCommonsPalletApi>::Currency; @@ -390,21 +390,18 @@ mod pallet { Ok(disputes.len() == T::MaxDisputes::get() as usize) } - fn on_global_dispute( - #[allow(dead_code, unused)] market_id: &Self::MarketId, - market: &MarketOf, - ) -> DispatchResult { + fn on_global_dispute(_market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); #[cfg(feature = "with-global-disputes")] { - let disputes = >::get(market_id); + let disputes = >::get(_market_id); // add report outcome to voting choices if let Some(report) = &market.report { T::GlobalDisputes::push_voting_outcome( - market_id, + _market_id, report.outcome.clone(), &report.by, >::zero(), @@ -412,7 +409,7 @@ mod pallet { } for MarketDispute { at: _, by, outcome, bond } in disputes.iter() { - T::GlobalDisputes::push_voting_outcome(market_id, outcome.clone(), by, bond)?; + T::GlobalDisputes::push_voting_outcome(_market_id, outcome.clone(), by, *bond)?; } } Ok(()) From 2f12959b9feab778e9eb942ccc6232fba8df5d02 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 17 Jan 2023 12:08:51 +0100 Subject: [PATCH 027/368] fix pm benchmarks --- zrml/prediction-markets/src/benchmarks.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 622bad072..358b3a81e 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -250,6 +250,11 @@ benchmarks! { let pool_id = >::market_pool(&market_id)?; let disputor = account("disputor", 1, 0); + ::AssetManager::deposit( + Asset::Ztg, + &disputor, + u128::MAX.saturated_into(), + ).unwrap(); let _ = Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id)?; let market = >::market(&market_id)?; @@ -470,6 +475,11 @@ benchmarks! { let outcome = OutcomeReport::Scalar(0); let disputor = account("disputor", 1, 0); + ::AssetManager::deposit( + Asset::Ztg, + &disputor, + u128::MAX.saturated_into(), + ).unwrap(); Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id)?; let now = >::block_number(); @@ -515,6 +525,11 @@ benchmarks! { })?; let disputor = account("disputor", 1, 0); + ::AssetManager::deposit( + Asset::Ztg, + &disputor, + u128::MAX.saturated_into(), + ).unwrap(); Pallet::::dispute(RawOrigin::Signed(disputor).into(), market_id)?; // Authorize the outcome with the highest number of correct reporters to maximize the @@ -786,6 +801,11 @@ benchmarks! { } let disputor: T::AccountId = account("Disputor", 1, 0); + ::AssetManager::deposit( + Asset::Ztg, + &disputor, + u128::MAX.saturated_into(), + ).unwrap(); let _ = Call::::dispute { market_id, } From 6f10d96e2aecce83998dfb7cac26705ad2dc122a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 17 Jan 2023 14:28:30 +0100 Subject: [PATCH 028/368] add migration --- runtime/common/src/lib.rs | 1 - zrml/prediction-markets/src/lib.rs | 2 - zrml/prediction-markets/src/migrations.rs | 203 +++++++++++++++++++++- zrml/prediction-markets/src/mock.rs | 1 - zrml/simple-disputes/src/lib.rs | 6 +- zrml/simple-disputes/src/mock.rs | 1 - 6 files changed, 204 insertions(+), 10 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 84b717a55..d7801dc17 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1051,7 +1051,6 @@ macro_rules! impl_config_traits { type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; - type PredictionMarketsPalletId = PmPalletId; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 043b8f884..d05f66614 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1591,8 +1591,6 @@ mod pallet { MarketStartTooSoon, /// The point in time when the market becomes active is too late. MarketStartTooLate, - /// The maximum number of disputes has been reached. - MaxDisputesReached, /// The market dispute mechanism has not failed. MarketDisputeMechanismNotFailed, /// Tried to settle missing bond. diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index 49aee5ec3..4cf93a34e 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -430,7 +430,7 @@ mod tests { use alloc::string::ToString; use frame_support::{migration::storage_key_iter, Twox64Concat}; use frame_system::pallet_prelude::BlockNumberFor; -use sp_runtime::traits::Saturating; +use sp_runtime::{traits::Saturating, SaturatedConversion}; use zeitgeist_primitives::types::AuthorityReport; use zrml_authorized::Pallet as AuthorizedPallet; @@ -704,6 +704,207 @@ mod tests_authorized { } } +use frame_support::dispatch::EncodeLike; +use zeitgeist_primitives::types::{MarketDispute, OldMarketDispute}; + +const PREDICTION_MARKETS_REQUIRED_STORAGE_VERSION: u16 = 7; +const PREDICTION_MARKETS_NEXT_STORAGE_VERSION: u16 = 8; + +#[cfg(feature = "try-runtime")] +type OldDisputesOf = frame_support::BoundedVec< + OldMarketDispute< + ::AccountId, + ::BlockNumber, + >, + ::MaxDisputes, +>; + +pub struct MoveDataToSimpleDisputes(PhantomData); + +impl OnRuntimeUpgrade + for MoveDataToSimpleDisputes +where + ::MarketId: EncodeLike< + <::MarketCommons as MarketCommonsPalletApi>::MarketId, + >, +{ + fn on_runtime_upgrade() -> Weight { + use orml_traits::NamedMultiReservableCurrency; + use zeitgeist_primitives::types::Asset; + + let mut total_weight = T::DbWeight::get().reads(1); + let pm_version = StorageVersion::get::>(); + if pm_version != PREDICTION_MARKETS_REQUIRED_STORAGE_VERSION { + log::info!( + "MoveDataToSimpleDisputes: market-commons version is {:?}, but {:?} is required", + pm_version, + PREDICTION_MARKETS_REQUIRED_STORAGE_VERSION, + ); + return total_weight; + } + log::info!("MoveDataToSimpleDisputes: Starting..."); + + total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); + + for (market_id, old_disputes) in crate::Disputes::::drain() { + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); + + total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); + if let Ok(mut market) = >::market(&market_id) { + match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + if let Some(first_dispute) = old_disputes.first() { + let OldMarketDispute { at: _, by, outcome: _ } = first_dispute; + market.bonds.dispute = + Some(Bond::new(by.clone(), T::DisputeBond::get())); + zrml_market_commons::Markets::::insert(market_id, market); + } else { + log::warn!( + "MoveDataToSimpleDisputes: Could not find first dispute for \ + market id {:?}", + market_id + ); + } + // for authorized use the first dispute as actual dispute caller + continue; + } + // for simple-disputes ignore who called the dispute the first time + // and just use the below code to fill Disputes inside simple-disputes + MarketDisputeMechanism::SimpleDisputes => (), + // ignore / delete all disputes for court markets + MarketDisputeMechanism::Court => continue, + } + } else { + log::warn!( + "MoveDataToSimpleDisputes: Could not find market with market id {:?}", + market_id + ); + } + let mut new_disputes = zrml_simple_disputes::Disputes::::get(market_id); + for (i, old_dispute) in old_disputes.iter().enumerate() { + let bond = zrml_simple_disputes::default_outcome_bond::(i); + let new_dispute = MarketDispute { + at: old_dispute.at, + by: old_dispute.by.clone(), + outcome: old_dispute.outcome.clone(), + bond, + }; + new_disputes.try_push(new_dispute).expect("Failed to push to bounded vector!"); + + // switch to new reserve identifier for simple disputes + let sd_pallet_id = zeitgeist_primitives::constants::SD_PALLET_ID; + let sd_reserve_id = sd_pallet_id.0; + let pm_pallet_id = zeitgeist_primitives::constants::PM_PALLET_ID; + let pm_reserve_id = pm_pallet_id.0; + ::AssetManager::unreserve_named( + &pm_reserve_id, + Asset::Ztg, + &old_dispute.by, + bond.saturated_into::().saturated_into(), + ); + ::AssetManager::reserve_named( + &sd_reserve_id, + Asset::Ztg, + &old_dispute.by, + bond.saturated_into::().saturated_into(), + ) + .expect("Failed to reserve ZTG for dispute bond"); + } + + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); + zrml_simple_disputes::Disputes::::insert(market_id, new_disputes); + } + + StorageVersion::new(PREDICTION_MARKETS_NEXT_STORAGE_VERSION).put::>(); + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); + log::info!("MoveDataToSimpleDisputes: Done!"); + total_weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<(), &'static str> { + log::info!("MoveDataToSimpleDisputes: Start pre_upgrade!"); + + let old_disputes = crate::Disputes::::iter().collect::>(); + Self::set_temp_storage(old_disputes, "old_disputes"); + + Ok(()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade() -> Result<(), &'static str> { + let old_disputes: BTreeMap, OldDisputesOf> = + Self::get_temp_storage("old_disputes").unwrap(); + + log::info!("MoveDataToSimpleDisputes: (post_upgrade) Start first try-runtime part!"); + + for (market_id, o) in old_disputes.iter() { + let market = >::market(&market_id) + .expect(&format!("Market for market id {:?} not found", market_id)[..]); + match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + let first_dispute = old_disputes + .first() + .expect(&format!("First dispute for market {:?} not found", market_id)[..]); + let disputor = first_dispute.by.clone(); + let bond = T::DisputeBond::get(); + assert_eq!( + market.bonds.dispute, + Some(Bond { who: disputor, value: bond, is_settled: false }), + ); + + let simple_disputes_count = + zrml_simple_disputes::Disputes::::get(market_id).iter().count(); + assert_eq!(simple_disputes_count, 0); + continue; + } + MarketDisputeMechanism::SimpleDisputes => { + let new_count = + zrml_simple_disputes::Disputes::::get(market_id).iter().count(); + let old_count = o.iter().count(); + assert_eq!(new_count, old_count); + } + MarketDisputeMechanism::Court => { + panic!("Court should not be contained at all.") + } + } + } + + log::info!("MoveDataToSimpleDisputes: (post_upgrade) Start second try-runtime part!"); + + assert!(crate::Disputes::::iter().count() == 0); + + for (market_id, new_disputes) in zrml_simple_disputes::Disputes::::iter() { + let old_disputes = old_disputes + .get(&market_id) + .expect(&format!("Disputes for market {:?} not found", market_id)[..]); + + let market = >::market(&market_id) + .expect(&format!("Market for market id {:?} not found", market_id)[..]); + match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + panic!("Authorized should not be contained in simple disputes."); + } + MarketDisputeMechanism::SimpleDisputes => (), + MarketDisputeMechanism::Court => { + panic!("Court should not be contained in simple disputes."); + } + } + + for (i, new_dispute) in new_disputes.iter().enumerate() { + let old_dispute = + old_disputes.get(i).expect(&format!("Dispute at index {} not found", i)[..]); + assert_eq!(new_dispute.at, old_dispute.at); + assert_eq!(new_dispute.by, old_dispute.by); + assert_eq!(new_dispute.outcome, old_dispute.outcome); + assert_eq!(new_dispute.bond, zrml_simple_disputes::default_outcome_bond::(i)); + } + } + + Ok(()) + } +} + // We use these utilities to prevent having to make the swaps pallet a dependency of // prediciton-markets. The calls are based on the implementation of `StorageVersion`, found here: // https://github.com/paritytech/substrate/blob/bc7a1e6c19aec92bfa247d8ca68ec63e07061032/frame/support/src/traits/metadata.rs#L168-L230 diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 9cfd75644..e9b15883c 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -314,7 +314,6 @@ impl zrml_simple_disputes::Config for Runtime { type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; - type PredictionMarketsPalletId = PmPalletId; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 5c8308654..89ba33201 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -110,9 +110,6 @@ mod pallet { #[pallet::constant] type PalletId: Get; - #[pallet::constant] - type PredictionMarketsPalletId: Get; - /// Weights generated by benchmarks type WeightInfo: WeightInfoZeitgeist; } @@ -169,6 +166,7 @@ mod pallet { OutcomeMismatch, CannotDisputeSameOutcome, MarketIsNotReported, + /// The maximum number of disputes has been reached. MaxDisputesReached, } @@ -225,7 +223,7 @@ mod pallet { impl Pallet { #[inline] pub fn reserve_id() -> [u8; 8] { - T::PredictionMarketsPalletId::get().0 + T::PalletId::get().0 } fn ensure_can_not_dispute_the_same_outcome( diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index e9872c558..e9219ddbb 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -137,7 +137,6 @@ impl crate::Config for Runtime { type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; - type PredictionMarketsPalletId = PmPalletId; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } From 9f002a40d023aa9d41c811a2136092b7ccfe4566 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 17 Jan 2023 16:31:57 +0100 Subject: [PATCH 029/368] simplify migration --- zrml/prediction-markets/src/migrations.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index 4cf93a34e..8610a6586 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -839,8 +839,11 @@ where log::info!("MoveDataToSimpleDisputes: (post_upgrade) Start first try-runtime part!"); for (market_id, o) in old_disputes.iter() { - let market = >::market(&market_id) + let market = ::MarketCommons::market(market_id) .expect(&format!("Market for market id {:?} not found", market_id)[..]); + + let disputes = zrml_simple_disputes::Disputes::::get(market_id); + match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { let first_dispute = old_disputes @@ -853,14 +856,12 @@ where Some(Bond { who: disputor, value: bond, is_settled: false }), ); - let simple_disputes_count = - zrml_simple_disputes::Disputes::::get(market_id).iter().count(); + let simple_disputes_count = disputes.iter().count(); assert_eq!(simple_disputes_count, 0); continue; } MarketDisputeMechanism::SimpleDisputes => { - let new_count = - zrml_simple_disputes::Disputes::::get(market_id).iter().count(); + let new_count = disputes.iter().count(); let old_count = o.iter().count(); assert_eq!(new_count, old_count); } @@ -876,10 +877,10 @@ where for (market_id, new_disputes) in zrml_simple_disputes::Disputes::::iter() { let old_disputes = old_disputes - .get(&market_id) + .get(&market_id.saturated_into::().saturated_into()) .expect(&format!("Disputes for market {:?} not found", market_id)[..]); - let market = >::market(&market_id) + let market = ::MarketCommons::market(&market_id) .expect(&format!("Market for market id {:?} not found", market_id)[..]); match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { From 09730a0a233d2871351573c3619d298eb170991b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 18 Jan 2023 08:06:55 +0100 Subject: [PATCH 030/368] correct migration --- zrml/prediction-markets/src/migrations.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index 8610a6586..a255a552f 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -839,14 +839,15 @@ where log::info!("MoveDataToSimpleDisputes: (post_upgrade) Start first try-runtime part!"); for (market_id, o) in old_disputes.iter() { - let market = ::MarketCommons::market(market_id) + let market = >::market(market_id) .expect(&format!("Market for market id {:?} not found", market_id)[..]); - let disputes = zrml_simple_disputes::Disputes::::get(market_id); + // market id is a reference, but we need the raw value to encode with the where clause + let disputes = zrml_simple_disputes::Disputes::::get(*market_id); match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - let first_dispute = old_disputes + let first_dispute = o .first() .expect(&format!("First dispute for market {:?} not found", market_id)[..]); let disputor = first_dispute.by.clone(); From f1347c3eaaf971af9c224913f19091c8ac9426c6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 18 Jan 2023 09:16:53 +0100 Subject: [PATCH 031/368] migration fixes and call filter --- runtime/common/src/lib.rs | 2 +- runtime/zeitgeist/src/lib.rs | 1 + zrml/prediction-markets/src/migrations.rs | 54 +++++++++++++++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index d7801dc17..df8fbdc6f 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -305,7 +305,7 @@ macro_rules! create_runtime { Court: zrml_court::{Call, Event, Pallet, Storage} = 52, LiquidityMining: zrml_liquidity_mining::{Call, Config, Event, Pallet, Storage} = 53, RikiddoSigmoidFeeMarketEma: zrml_rikiddo::::{Pallet, Storage} = 54, - SimpleDisputes: zrml_simple_disputes::{Event, Pallet, Storage} = 55, + SimpleDisputes: zrml_simple_disputes::{Call, Event, Pallet, Storage} = 55, Swaps: zrml_swaps::{Call, Event, Pallet, Storage} = 56, PredictionMarkets: zrml_prediction_markets::{Call, Event, Pallet, Storage} = 57, Styx: zrml_styx::{Call, Event, Pallet, Storage} = 58, diff --git a/runtime/zeitgeist/src/lib.rs b/runtime/zeitgeist/src/lib.rs index 745f6948e..c22fbc6a4 100644 --- a/runtime/zeitgeist/src/lib.rs +++ b/runtime/zeitgeist/src/lib.rs @@ -162,6 +162,7 @@ impl Contains for IsCallable { _ => true, } } + Call::SimpleDisputes(_) => false, Call::System(inner_call) => { match inner_call { // Some "waste" storage will never impact proper operation. diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index a255a552f..a6d9e2a19 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -753,10 +753,17 @@ where if let Ok(mut market) = >::market(&market_id) { match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { + // TODO Move this part of the migration (market mutation) + // TODO into the OutsiderBond migration (needs to run before this migration) + // TODO because it needs to iterate over all Disputes of the pm pallet + // TODO in this current migration we drain all Disputes of the pm pallet if let Some(first_dispute) = old_disputes.first() { let OldMarketDispute { at: _, by, outcome: _ } = first_dispute; market.bonds.dispute = Some(Bond::new(by.clone(), T::DisputeBond::get())); + + total_weight = + total_weight.saturating_add(T::DbWeight::get().writes(1)); zrml_market_commons::Markets::::insert(market_id, market); } else { log::warn!( @@ -780,6 +787,8 @@ where market_id ); } + + total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); let mut new_disputes = zrml_simple_disputes::Disputes::::get(market_id); for (i, old_dispute) in old_disputes.iter().enumerate() { let bond = zrml_simple_disputes::default_outcome_bond::(i); @@ -789,26 +798,62 @@ where outcome: old_dispute.outcome.clone(), bond, }; - new_disputes.try_push(new_dispute).expect("Failed to push to bounded vector!"); + let res = new_disputes.try_push(new_dispute); + if res.is_err() { + log::error!( + "MoveDataToSimpleDisputes: Could not push dispute for market id {:?}", + market_id + ); + } // switch to new reserve identifier for simple disputes let sd_pallet_id = zeitgeist_primitives::constants::SD_PALLET_ID; let sd_reserve_id = sd_pallet_id.0; let pm_pallet_id = zeitgeist_primitives::constants::PM_PALLET_ID; let pm_reserve_id = pm_pallet_id.0; + + // charge weight defensivly for unreserve_named + // https://github.com/open-web3-stack/open-runtime-module-library/blob/24f0a8b6e04e1078f70d0437fb816337cdf4f64c/tokens/src/lib.rs#L1516-L1547 + total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(4, 3)); + let reserved_balance = ::AssetManager::reserved_balance_named( + &pm_reserve_id, + Asset::Ztg, + &old_dispute.by, + ); + if reserved_balance < bond.saturated_into::().saturated_into() { + log::error!( + "MoveDataToSimpleDisputes: Could not unreserve {:?} for {:?} because \ + reserved balance is only {:?}. Market id: {:?}", + bond, + old_dispute.by, + reserved_balance, + market_id, + ); + } ::AssetManager::unreserve_named( &pm_reserve_id, Asset::Ztg, &old_dispute.by, bond.saturated_into::().saturated_into(), ); - ::AssetManager::reserve_named( + + // charge weight defensivly for reserve_named + // https://github.com/open-web3-stack/open-runtime-module-library/blob/24f0a8b6e04e1078f70d0437fb816337cdf4f64c/tokens/src/lib.rs#L1486-L1499 + total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(3, 3)); + let res = ::AssetManager::reserve_named( &sd_reserve_id, Asset::Ztg, &old_dispute.by, bond.saturated_into::().saturated_into(), - ) - .expect("Failed to reserve ZTG for dispute bond"); + ); + if res.is_err() { + log::error!( + "MoveDataToSimpleDisputes: Could not reserve bond for dispute caller {:?} \ + and market id {:?}", + old_dispute.by, + market_id + ); + } } total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); @@ -903,6 +948,7 @@ where } } + log::info!("MoveDataToSimpleDisputes: Done! (post_upgrade)"); Ok(()) } } From b42002c1292afbad80df931d1ac08ab82652eac8 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 18 Jan 2023 10:49:09 +0100 Subject: [PATCH 032/368] correct admin_destroy_market benchmark --- primitives/src/traits/dispute_api.rs | 1 - zrml/authorized/src/mock.rs | 2 - zrml/court/src/mock.rs | 1 - zrml/prediction-markets/src/benchmarks.rs | 20 ++++++---- zrml/prediction-markets/src/lib.rs | 5 +-- zrml/prediction-markets/src/weights.rs | 45 +++++++++++++---------- zrml/simple-disputes/src/mock.rs | 1 - 7 files changed, 39 insertions(+), 36 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index b7e576cd5..880698a71 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -112,7 +112,6 @@ pub trait DisputeResolutionApi { type Balance; type BlockNumber; type MarketId; - type MaxDisputes; type Moment; /// Resolve a market. diff --git a/zrml/authorized/src/mock.rs b/zrml/authorized/src/mock.rs index adef4d071..8027749b8 100644 --- a/zrml/authorized/src/mock.rs +++ b/zrml/authorized/src/mock.rs @@ -66,7 +66,6 @@ construct_runtime!( ord_parameter_types! { pub const AuthorizedDisputeResolutionUser: AccountIdTest = ALICE; - pub const MaxDisputes: u32 = 64; } // MockResolution implements DisputeResolutionApi with no-ops. @@ -77,7 +76,6 @@ impl DisputeResolutionApi for MockResolution { type Balance = Balance; type BlockNumber = BlockNumber; type MarketId = MarketId; - type MaxDisputes = MaxDisputes; type Moment = Moment; fn resolve( diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index bc41187cf..88bb89d8a 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -74,7 +74,6 @@ impl DisputeResolutionApi for NoopResolution { type Balance = Balance; type BlockNumber = BlockNumber; type MarketId = MarketId; - type MaxDisputes = u32; type Moment = Moment; fn resolve( diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 358b3a81e..67faa12a5 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -227,7 +227,7 @@ benchmarks! { From<::MarketId>, } - admin_destroy_disputed_market{ + admin_destroy_disputed_market { // The number of assets. let a in (T::MinCategories::get().into())..T::MaxCategories::get().into(); // The number of market ids per open time frame. @@ -265,16 +265,18 @@ benchmarks! { }; for i in 0..o { + // shift of 1 to avoid collisions with first market id 0 MarketIdsPerOpenTimeFrame::::try_mutate( Pallet::::calculate_time_frame_of_moment(range_start), - |ids| ids.try_push(i.into()), + |ids| ids.try_push((i + 1).into()), ).unwrap(); } for i in 0..c { + // shift of 65 to avoid collisions with `o` MarketIdsPerCloseTimeFrame::::try_mutate( Pallet::::calculate_time_frame_of_moment(range_end), - |ids| ids.try_push(i.into()), + |ids| ids.try_push((i + 65).into()), ).unwrap(); } @@ -287,9 +289,10 @@ benchmarks! { let now = >::block_number(); let resolves_at = now.saturating_add(::CorrectionPeriod::get()); for i in 0..r { + // shift of 129 to avoid collisions with `o` and `c` MarketIdsPerDisputeBlock::::try_mutate( resolves_at, - |ids| ids.try_push(i.into()), + |ids| ids.try_push((i + 129).into()), ).unwrap(); } @@ -326,25 +329,28 @@ benchmarks! { }; for i in 0..o { + // shift of 1 to avoid collisions with first market id 0 MarketIdsPerOpenTimeFrame::::try_mutate( Pallet::::calculate_time_frame_of_moment(range_start), - |ids| ids.try_push(i.into()), + |ids| ids.try_push((i + 1).into()), ).unwrap(); } for i in 0..c { + // shift of 65 to avoid collisions with `o` MarketIdsPerCloseTimeFrame::::try_mutate( Pallet::::calculate_time_frame_of_moment(range_end), - |ids| ids.try_push(i.into()), + |ids| ids.try_push((i + 65).into()), ).unwrap(); } let report_at = market.report.unwrap().at; let resolves_at = report_at.saturating_add(market.deadlines.dispute_duration); for i in 0..r { + // shift of 129 to avoid collisions with `o` and `c` MarketIdsPerReportBlock::::try_mutate( resolves_at, - |ids| ids.try_push(i.into()), + |ids| ids.try_push((i + 129).into()), ).unwrap(); } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index d05f66614..296cd3021 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -205,7 +205,6 @@ mod pallet { ) .max(T::WeightInfo::admin_destroy_disputed_market( T::MaxCategories::get().into(), - T::MaxDisputes::get(), CacheSize::get(), CacheSize::get(), CacheSize::get(), @@ -265,7 +264,7 @@ mod pallet { let open_ids_len = Self::clear_auto_open(&market_id)?; let close_ids_len = Self::clear_auto_close(&market_id)?; - let (ids_len, disputes_len) = Self::clear_auto_resolve(&market_id)?; + let (ids_len, _) = Self::clear_auto_resolve(&market_id)?; Self::clear_dispute_mechanism(&market_id)?; >::remove_market(&market_id)?; @@ -288,7 +287,6 @@ mod pallet { Ok(( Some(T::WeightInfo::admin_destroy_disputed_market( category_count, - disputes_len, open_ids_len, close_ids_len, ids_len, @@ -2822,7 +2820,6 @@ mod pallet { type Balance = BalanceOf; type BlockNumber = T::BlockNumber; type MarketId = MarketIdOf; - type MaxDisputes = T::MaxDisputes; type Moment = MomentOf; fn resolve( diff --git a/zrml/prediction-markets/src/weights.rs b/zrml/prediction-markets/src/weights.rs index 496d959a4..153708e97 100644 --- a/zrml/prediction-markets/src/weights.rs +++ b/zrml/prediction-markets/src/weights.rs @@ -45,7 +45,7 @@ use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within /// zrml_prediction_markets (automatically generated) pub trait WeightInfoZeitgeist { - fn admin_destroy_disputed_market(a: u32, d: u32, o: u32, c: u32, r: u32) -> Weight; + fn admin_destroy_disputed_market(a: u32, o: u32, c: u32, r: u32) -> Weight; fn admin_destroy_reported_market(a: u32, o: u32, c: u32, r: u32) -> Weight; fn admin_move_market_to_closed(o: u32, c: u32) -> Weight; fn admin_move_market_to_resolved_scalar_reported(r: u32) -> Weight; @@ -83,25 +83,27 @@ pub trait WeightInfoZeitgeist { pub struct WeightInfo(PhantomData); impl WeightInfoZeitgeist for WeightInfo { // Storage: MarketCommons Markets (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: System Account (r:2 w:2) + // Storage: Balances Reserves (r:2 w:2) + // Storage: System Account (r:3 w:3) // Storage: MarketCommons MarketPool (r:1 w:1) // Storage: Swaps Pools (r:1 w:1) // Storage: Tokens Accounts (r:2 w:2) // Storage: Tokens TotalIssuance (r:2 w:2) - // Storage: PredictionMarkets Disputes (r:1 w:1) + // Storage: Authorized AuthorizedOutcomeReports (r:1 w:1) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) - fn admin_destroy_disputed_market(a: u32, d: u32, o: u32, _c: u32, _r: u32) -> Weight { - (131_391_000 as Weight) - // Standard Error: 3_000 - .saturating_add((22_410_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 56_000 - .saturating_add((1_219_000 as Weight).saturating_mul(d as Weight)) - // Standard Error: 3_000 - .saturating_add((66_000 as Weight).saturating_mul(o as Weight)) - .saturating_add(T::DbWeight::get().reads(8 as Weight)) + fn admin_destroy_disputed_market(a: u32, o: u32, c: u32, r: u32) -> Weight { + (138_848_000 as Weight) + // Standard Error: 35_000 + .saturating_add((20_922_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 34_000 + .saturating_add((1_091_000 as Weight).saturating_mul(o as Weight)) + // Standard Error: 34_000 + .saturating_add((984_000 as Weight).saturating_mul(c as Weight)) + // Standard Error: 34_000 + .saturating_add((1_026_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(a as Weight))) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(10 as Weight)) .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(a as Weight))) } // Storage: MarketCommons Markets (r:1 w:1) @@ -112,14 +114,17 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Tokens Accounts (r:2 w:2) // Storage: Tokens TotalIssuance (r:2 w:2) // Storage: PredictionMarkets MarketIdsPerReportBlock (r:1 w:1) - // Storage: PredictionMarkets Disputes (r:0 w:1) - fn admin_destroy_reported_market(a: u32, _o: u32, _c: u32, _r: u32) -> Weight { - (148_923_000 as Weight) - // Standard Error: 4_000 - .saturating_add((22_300_000 as Weight).saturating_mul(a as Weight)) + fn admin_destroy_reported_market(a: u32, o: u32, c: u32, _r: u32) -> Weight { + (144_950_000 as Weight) + // Standard Error: 5_000 + .saturating_add((23_098_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 5_000 + .saturating_add((41_000 as Weight).saturating_mul(o as Weight)) + // Standard Error: 5_000 + .saturating_add((43_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(a as Weight))) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(a as Weight))) } // Storage: MarketCommons Markets (r:1 w:1) diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index e9219ddbb..b6ae69469 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -100,7 +100,6 @@ impl DisputeResolutionApi for NoopResolution { type Balance = Balance; type BlockNumber = BlockNumber; type MarketId = MarketId; - type MaxDisputes = u32; type Moment = Moment; fn resolve( From 80b3fa6bec13c93833758bdb99e1f5b76c7b0aed Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 18 Jan 2023 10:56:15 +0100 Subject: [PATCH 033/368] improve simple-disputes mock --- zrml/simple-disputes/src/mock.rs | 61 ++++++++++++++++++------------- zrml/simple-disputes/src/tests.rs | 8 ++-- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index b6ae69469..ddf72f35b 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -21,18 +21,16 @@ use crate::{self as zrml_simple_disputes}; use frame_support::{ construct_runtime, ord_parameter_types, pallet_prelude::{DispatchError, Weight}, - traits::{Everything, NeverEnsureOrigin}, + traits::Everything, }; -use frame_system::EnsureSignedBy; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, ExistentialDeposits, GetNativeCurrencyId, MaxApprovals, MaxDisputes, - MaxReserves, MinimumPeriod, OutcomeBond, OutcomeFactor, PmPalletId, SimpleDisputesPalletId, - TreasuryPalletId, + BlockHashCount, ExistentialDeposits, GetNativeCurrencyId, MaxDisputes, MaxReserves, + MinimumPeriod, OutcomeBond, OutcomeFactor, PmPalletId, SimpleDisputesPalletId, BASE, }, traits::DisputeResolutionApi, types::{ @@ -47,8 +45,16 @@ use zeitgeist_primitives::constants::mock::{ MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, }; +pub const ALICE: AccountIdTest = 0; +pub const BOB: AccountIdTest = 1; +pub const CHARLIE: AccountIdTest = 2; +pub const DAVE: AccountIdTest = 3; +pub const EVE: AccountIdTest = 4; +pub const FRED: AccountIdTest = 5; pub const SUDO: AccountIdTest = 69; +pub const INITIAL_BALANCE: u128 = 1_000 * BASE; + ord_parameter_types! { pub const Sudo: AccountIdTest = SUDO; } @@ -69,7 +75,6 @@ construct_runtime!( System: frame_system::{Call, Config, Event, Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet}, Tokens: orml_tokens::{Config, Event, Pallet, Storage}, - Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, } ); @@ -88,7 +93,6 @@ construct_runtime!( System: frame_system::{Call, Config, Event, Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet}, Tokens: orml_tokens::{Config, Event, Pallet, Storage}, - Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, } ); @@ -230,29 +234,34 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } -impl pallet_treasury::Config for Runtime { - type ApproveOrigin = EnsureSignedBy; - type Burn = (); - type BurnDestination = (); - type Currency = Balances; - type Event = (); - type MaxApprovals = MaxApprovals; - type OnSlash = (); - type PalletId = TreasuryPalletId; - type ProposalBond = (); - type ProposalBondMinimum = (); - type ProposalBondMaximum = (); - type RejectOrigin = EnsureSignedBy; - type SpendFunds = (); - type SpendOrigin = NeverEnsureOrigin; - type SpendPeriod = (); - type WeightInfo = (); +pub struct ExtBuilder { + balances: Vec<(AccountIdTest, Balance)>, } -pub struct ExtBuilder; +impl Default for ExtBuilder { + fn default() -> Self { + Self { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (BOB, INITIAL_BALANCE), + (CHARLIE, INITIAL_BALANCE), + (DAVE, INITIAL_BALANCE), + (EVE, INITIAL_BALANCE), + (FRED, INITIAL_BALANCE), + (SUDO, INITIAL_BALANCE), + ], + } + } +} impl ExtBuilder { pub fn build(self) -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + pallet_balances::GenesisConfig:: { balances: self.balances } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() } } diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index ff8a0170c..b1bc24304 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -50,7 +50,7 @@ const DEFAULT_MARKET: MarketOf = Market { #[test] fn on_dispute_denies_non_simple_disputes_markets() { - ExtBuilder.build().execute_with(|| { + ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::Court; assert_noop!( @@ -62,7 +62,7 @@ fn on_dispute_denies_non_simple_disputes_markets() { #[test] fn get_resolution_outcome_denies_non_simple_disputes_markets() { - ExtBuilder.build().execute_with(|| { + ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::Court; assert_noop!( @@ -74,7 +74,7 @@ fn get_resolution_outcome_denies_non_simple_disputes_markets() { #[test] fn get_resolution_outcome_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outcome() { - ExtBuilder.build().execute_with(|| { + ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; market.status = MarketStatus::Disputed; let disputes = BoundedVec::try_from( @@ -102,3 +102,5 @@ fn get_resolution_outcome_sets_the_last_dispute_of_disputed_markets_as_the_canon ) }); } + +// TODO test `reserve_outcome` functionality and API functionality From bd7c97d9c66867be496a93c4b272ffd761785a40 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 19 Jan 2023 11:12:58 +0100 Subject: [PATCH 034/368] benchmark reserve_outcome --- runtime/common/src/lib.rs | 2 + zrml/simple-disputes/src/benchmarks.rs | 69 +++++++++++++++++++++++++ zrml/simple-disputes/src/lib.rs | 70 ++++++++++++++++++++------ 3 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 zrml/simple-disputes/src/benchmarks.rs diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index df8fbdc6f..8daf02710 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1211,6 +1211,7 @@ macro_rules! create_runtime_api { list_benchmark!(list, extra, zrml_swaps, Swaps); list_benchmark!(list, extra, zrml_authorized, Authorized); list_benchmark!(list, extra, zrml_court, Court); + list_benchmark!(list, extra, zrml_simple_disputes, SimpleDisputes); #[cfg(feature = "with-global-disputes")] list_benchmark!(list, extra, zrml_global_disputes, GlobalDisputes); list_benchmark!(list, extra, zrml_prediction_markets, PredictionMarkets); @@ -1289,6 +1290,7 @@ macro_rules! create_runtime_api { add_benchmark!(params, batches, zrml_swaps, Swaps); add_benchmark!(params, batches, zrml_authorized, Authorized); add_benchmark!(params, batches, zrml_court, Court); + add_benchmark!(params, batches, zrml_simple_disputes, SimpleDisputes); #[cfg(feature = "with-global-disputes")] add_benchmark!(params, batches, zrml_global_disputes, GlobalDisputes); add_benchmark!(params, batches, zrml_prediction_markets, PredictionMarkets); diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs new file mode 100644 index 000000000..a77238632 --- /dev/null +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -0,0 +1,69 @@ +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +#![allow( + // Auto-generated code is a no man's land + clippy::integer_arithmetic +)] +#![allow(clippy::type_complexity)] +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use frame_benchmarking::{account, benchmarks, vec, whitelisted_caller}; + +benchmarks! { + reserve_outcome { + let d in 1..(T::MaxDisputes::get() - 1); + let e in 1..63; + + let caller: T::AccountId = whitelisted_caller(); + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market).unwrap(); + + let now = >::block_number(); + + for i in 0..d { + + } + let last_dispute = MarketDispute { + at: now, + by: caller.clone(), + outcome: OutcomeReport::Scalar(2), + bond: default_outcome_bond::(0usize), + }; + Disputes::::insert(market_id, last_dispute); + + + let dispute_duration_ends_at_block = + now.saturating_add(market.deadlines.dispute_duration); + for i in 0..e { + let id = T::MarketCommons::push_market(market_mock::()).unwrap(); + T::DisputeResolution::add_auto_resolve(&id, dispute_duration_ends_at_block).unwrap(); + } + + let outcome = OutcomeReport::Scalar(1); + let bond = default_outcome_bond::(0usize); + let _ = T::Currency::deposit_creating(&caller, bond); + }: _(RawOrigin::Signed(caller.clone()), market_id, outcome) + + impl_benchmark_test_suite!( + PredictionMarket, + crate::mock::ExtBuilder::default().build(), + crate::mock::Runtime, + ); +} diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 89ba33201..4cbe1b1f9 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -20,6 +20,8 @@ extern crate alloc; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarks; mod mock; mod simple_disputes_pallet_api; mod tests; @@ -27,9 +29,16 @@ pub mod weights; pub use pallet::*; pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; +use zeitgeist_primitives::{ + traits::{DisputeApi, DisputeResolutionApi, ZeitgeistAssetManager}, + types::{ + Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport, Report, + }, +}; #[frame_support::pallet] mod pallet { + use super::*; use crate::{weights::WeightInfoZeitgeist, SimpleDisputesPalletApi}; use alloc::vec::Vec; use core::marker::PhantomData; @@ -48,13 +57,7 @@ mod pallet { traits::{CheckedDiv, Saturating}, DispatchError, SaturatedConversion, }; - use zeitgeist_primitives::{ - traits::{DisputeApi, DisputeResolutionApi, ZeitgeistAssetManager}, - types::{ - Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport, - Report, - }, - }; + #[cfg(feature = "with-global-disputes")] use zrml_global_disputes::GlobalDisputesPalletApi; use zrml_market_commons::MarketCommonsPalletApi; @@ -128,6 +131,14 @@ mod pallet { ::BlockNumber, MomentOf, >; + pub(crate) type DisputesOf = BoundedVec< + MarketDispute< + ::AccountId, + ::BlockNumber, + BalanceOf, + >, + ::MaxDisputes, + >; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -135,13 +146,8 @@ mod pallet { /// For each market, this holds the dispute information for each dispute that's /// been issued. #[pallet::storage] - pub type Disputes = StorageMap< - _, - Blake2_128Concat, - MarketIdOf, - BoundedVec>, T::MaxDisputes>, - ValueQuery, - >; + pub type Disputes = + StorageMap<_, Blake2_128Concat, MarketIdOf, DisputesOf, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] @@ -446,3 +452,39 @@ mod pallet { ) } } + +#[cfg(any(feature = "runtime-benchmarks", test))] +pub(crate) fn market_mock() +-> zeitgeist_primitives::types::Market, T::BlockNumber, MomentOf> +where + T: crate::Config, +{ + use frame_support::traits::Get; + use sp_runtime::{traits::AccountIdConversion, SaturatedConversion}; + use zeitgeist_primitives::types::{MarketBonds, ScoringRule}; + + zeitgeist_primitives::types::Market { + creation: zeitgeist_primitives::types::MarketCreation::Permissionless, + creator_fee: 0, + creator: T::PalletId::get().into_account_truncating(), + market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=100), + dispute_mechanism: zeitgeist_primitives::types::MarketDisputeMechanism::SimpleDisputes, + metadata: Default::default(), + oracle: T::PalletId::get().into_account_truncating(), + period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), + deadlines: zeitgeist_primitives::types::Deadlines { + grace_period: 1_u32.into(), + oracle_duration: 1_u32.into(), + dispute_duration: 42_u32.into(), + }, + report: Some(zeitgeist_primitives::types::Report { + outcome: OutcomeReport::Scalar(0), + at: 0u64.saturated_into(), + by: T::PalletId::get().into_account_truncating(), + }), + resolved_outcome: None, + scoring_rule: ScoringRule::CPMM, + status: zeitgeist_primitives::types::MarketStatus::Disputed, + bonds: MarketBonds::default(), + } +} From 355dad64820394bebafeca0e8bd7c0a53520a6f7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 19 Jan 2023 13:39:09 +0100 Subject: [PATCH 035/368] benchmark reserve_outcome --- primitives/src/traits/dispute_api.rs | 3 +- zrml/authorized/src/lib.rs | 4 +- zrml/authorized/src/mock.rs | 4 +- zrml/court/src/lib.rs | 4 +- zrml/court/src/mock.rs | 4 +- zrml/prediction-markets/src/migrations.rs | 1 - zrml/simple-disputes/src/benchmarks.rs | 48 +++++++++++++++++------ zrml/simple-disputes/src/lib.rs | 27 ++++++++----- zrml/simple-disputes/src/mock.rs | 4 +- zrml/simple-disputes/src/weights.rs | 44 +++++++++++---------- 10 files changed, 89 insertions(+), 54 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index d61635d6b..3ef91f806 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -16,11 +16,10 @@ // along with Zeitgeist. If not, see . use crate::{ - market::MarketDispute, outcome_report::OutcomeReport, types::{Asset, Market}, }; -use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight, BoundedVec}; +use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight}; use parity_scale_codec::MaxEncodedLen; use sp_runtime::DispatchError; diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 41644901c..efdd3ae40 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -46,7 +46,9 @@ mod pallet { use sp_runtime::{traits::Saturating, DispatchError}; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, - types::{Asset, AuthorityReport, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport}, + types::{ + Asset, AuthorityReport, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport, + }, }; use zrml_market_commons::MarketCommonsPalletApi; diff --git a/zrml/authorized/src/mock.rs b/zrml/authorized/src/mock.rs index 702d57522..892ad49ac 100644 --- a/zrml/authorized/src/mock.rs +++ b/zrml/authorized/src/mock.rs @@ -38,8 +38,8 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, Moment, - UncheckedExtrinsicTest, + AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, + Moment, UncheckedExtrinsicTest, }, }; diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index ed7ca0e36..b6254ff28 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -66,7 +66,9 @@ mod pallet { }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, - types::{Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport}, + types::{ + Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport, + }, }; use zrml_market_commons::MarketCommonsPalletApi; diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index f06ea7002..79c6e2761 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -36,8 +36,8 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, Moment, - UncheckedExtrinsicTest, + AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, + Moment, UncheckedExtrinsicTest, }, }; diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index 998f35fef..da96ef3a7 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -749,7 +749,6 @@ where { fn on_runtime_upgrade() -> Weight { use orml_traits::NamedMultiReservableCurrency; - use zeitgeist_primitives::types::Asset; let mut total_weight = T::DbWeight::get().reads(1); let pm_version = StorageVersion::get::>(); diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index a77238632..dc97b753c 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -22,32 +22,54 @@ #![allow(clippy::type_complexity)] #![cfg(feature = "runtime-benchmarks")] +#[cfg(test)] +use crate::Pallet as SimpleDisputes; + use super::*; -use frame_benchmarking::{account, benchmarks, vec, whitelisted_caller}; +use frame_benchmarking::{account, benchmarks, whitelisted_caller, Vec}; +use frame_support::{dispatch::RawOrigin, traits::Get}; +use orml_traits::MultiCurrency; +use sp_runtime::traits::{One, Saturating}; +use zrml_market_commons::MarketCommonsPalletApi; benchmarks! { reserve_outcome { let d in 1..(T::MaxDisputes::get() - 1); + let r in 1..63; let e in 1..63; let caller: T::AccountId = whitelisted_caller(); let market_id = 0u32.into(); let market = market_mock::(); - T::MarketCommons::push_market(market).unwrap(); + T::MarketCommons::push_market(market.clone()).unwrap(); - let now = >::block_number(); + let mut now; + let mut disputes = Vec::new(); for i in 0..d { + now = >::block_number(); + let disputor = account("disputor", i, 0); + let last_dispute = MarketDispute { + at: now, + by: disputor, + outcome: OutcomeReport::Scalar((2 + i).into()), + bond: default_outcome_bond::(i as usize), + }; + disputes.push(last_dispute); + >::set_block_number(now.saturating_add(T::BlockNumber::one())); + } + let last_dispute = disputes.last().unwrap(); + let auto_resolve = last_dispute.at.saturating_add(market.deadlines.dispute_duration); + for i in 0..r { + let id = T::MarketCommons::push_market(market_mock::()).unwrap(); + T::DisputeResolution::add_auto_resolve(&id, auto_resolve).unwrap(); } - let last_dispute = MarketDispute { - at: now, - by: caller.clone(), - outcome: OutcomeReport::Scalar(2), - bond: default_outcome_bond::(0usize), - }; - Disputes::::insert(market_id, last_dispute); + let now = >::block_number(); + + let bounded_vec = >::try_from(disputes).unwrap(); + Disputes::::insert(market_id, bounded_vec); let dispute_duration_ends_at_block = now.saturating_add(market.deadlines.dispute_duration); @@ -57,12 +79,12 @@ benchmarks! { } let outcome = OutcomeReport::Scalar(1); - let bond = default_outcome_bond::(0usize); - let _ = T::Currency::deposit_creating(&caller, bond); + let bond = default_outcome_bond::(T::MaxDisputes::get() as usize); + T::AssetManager::deposit(Asset::Ztg, &caller, bond).unwrap(); }: _(RawOrigin::Signed(caller.clone()), market_id, outcome) impl_benchmark_test_suite!( - PredictionMarket, + SimpleDisputes, crate::mock::ExtBuilder::default().build(), crate::mock::Runtime, ); diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 7832b1c31..3a496dbee 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -45,7 +45,9 @@ mod pallet { use frame_support::{ dispatch::DispatchResult, ensure, - pallet_prelude::{Blake2_128Concat, DispatchResultWithPostInfo, StorageMap, ValueQuery}, + pallet_prelude::{ + Blake2_128Concat, ConstU32, DispatchResultWithPostInfo, StorageMap, ValueQuery, + }, traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency}, transactional, BoundedVec, PalletId, }; @@ -140,6 +142,7 @@ mod pallet { >, ::MaxDisputes, >; + pub type CacheSize = ConstU32<64>; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -182,7 +185,11 @@ mod pallet { #[pallet::call] impl Pallet { - #[pallet::weight(5000)] + #[pallet::weight(T::WeightInfo::reserve_outcome( + T::MaxDisputes::get(), + CacheSize::get(), + CacheSize::get(), + ))] #[transactional] pub fn reserve_outcome( origin: OriginFor, @@ -216,14 +223,15 @@ mod pallet { })?; // each dispute resets dispute_duration - Self::remove_auto_resolve(disputes.as_slice(), &market_id, &market); + let r = Self::remove_auto_resolve(disputes.as_slice(), &market_id, &market); let dispute_duration_ends_at_block = now.saturating_add(market.deadlines.dispute_duration); - T::DisputeResolution::add_auto_resolve(&market_id, dispute_duration_ends_at_block)?; + let e = + T::DisputeResolution::add_auto_resolve(&market_id, dispute_duration_ends_at_block)?; Self::deposit_event(Event::OutcomeReserved { market_id, dispute: market_dispute }); - Ok((Some(5000)).into()) + Ok((Some(T::WeightInfo::reserve_outcome(num_disputes, r, e))).into()) } } @@ -266,13 +274,14 @@ mod pallet { disputes: &[MarketDispute>], market_id: &MarketIdOf, market: &MarketOf, - ) { + ) -> u32 { if let Some(dispute_duration_ends_at_block) = Self::get_auto_resolve(disputes, market) { - T::DisputeResolution::remove_auto_resolve( + return T::DisputeResolution::remove_auto_resolve( market_id, dispute_duration_ends_at_block, ); } + 0u32 } } @@ -455,8 +464,7 @@ mod pallet { } #[cfg(any(feature = "runtime-benchmarks", test))] -pub(crate) fn market_mock() --> zeitgeist_primitives::types::Market, T::BlockNumber, MomentOf> +pub(crate) fn market_mock() -> MarketOf where T: crate::Config, { @@ -465,6 +473,7 @@ where use zeitgeist_primitives::types::{MarketBonds, ScoringRule}; zeitgeist_primitives::types::Market { + base_asset: Asset::Ztg, creation: zeitgeist_primitives::types::MarketCreation::Permissionless, creator_fee: 0, creator: T::PalletId::get().into_account_truncating(), diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 4ec9d6d9c..13a47032d 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -34,8 +34,8 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, CurrencyId, - Hash, Index, Market, MarketId, Moment, UncheckedExtrinsicTest, + AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, + CurrencyId, Hash, Index, Market, MarketId, Moment, UncheckedExtrinsicTest, }, }; diff --git a/zrml/simple-disputes/src/weights.rs b/zrml/simple-disputes/src/weights.rs index 248c8f7e0..354a87e83 100644 --- a/zrml/simple-disputes/src/weights.rs +++ b/zrml/simple-disputes/src/weights.rs @@ -15,25 +15,25 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -//! Autogenerated weights for zrml_prediction_markets +//! Autogenerated weights for zrml_simple_disputes //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-09, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-01-19, STEPS: `10`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Native), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/release/zeitgeist +// ./target/debug/zeitgeist // benchmark // pallet // --chain=dev // --steps=10 -// --repeat=1000 -// --pallet=zrml_prediction_markets +// --repeat=10 +// --pallet=zrml_simple_disputes // --extrinsic=* -// --execution=wasm +// --execution=Native // --wasm-execution=compiled // --heap-pages=4096 -// --output=./zrml/prediction-markets/src/weights.rs +// --output=./zrml/simple-disputes/src/weights2.rs // --template=./misc/weight_template.hbs #![allow(unused_parens)] @@ -43,25 +43,27 @@ use core::marker::PhantomData; use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within -/// zrml_prediction_markets (automatically generated) +/// zrml_simple_disputes (automatically generated) pub trait WeightInfoZeitgeist { - fn start_global_dispute(m: u32, n: u32) -> Weight; + fn reserve_outcome(d: u32, r: u32, e: u32) -> Weight; } +/// Weight functions for zrml_simple_disputes (automatically generated) pub struct WeightInfo(PhantomData); impl WeightInfoZeitgeist for WeightInfo { // Storage: MarketCommons Markets (r:1 w:0) - // Storage: PredictionMarkets Disputes (r:1 w:0) - // Storage: GlobalDisputes Winners (r:1 w:1) - // Storage: GlobalDisputes Outcomes (r:7 w:7) + // Storage: SimpleDisputes Disputes (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) - fn start_global_dispute(m: u32, n: u32) -> Weight { - (94_106_000 as Weight) - // Standard Error: 0 - .saturating_add((15_000 as Weight).saturating_mul(m as Weight)) - // Standard Error: 0 - .saturating_add((20_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(12 as Weight)) - .saturating_add(T::DbWeight::get().writes(10 as Weight)) + fn reserve_outcome(d: u32, r: u32, e: u32) -> Weight { + (400_160_000 as Weight) + // Standard Error: 1_302_000 + .saturating_add((3_511_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 69_000 + .saturating_add((324_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 69_000 + .saturating_add((311_000 as Weight).saturating_mul(e as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } } From a43ee97cf45dbd5c1318602b3c80cf2f354891af Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 19 Jan 2023 15:27:32 +0100 Subject: [PATCH 036/368] correct merge conflicts --- zrml/global-disputes/src/lib.rs | 17 ++++++++++------- zrml/prediction-markets/src/lib.rs | 19 +++---------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 636414fa9..2499bfc97 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -51,9 +51,16 @@ mod pallet { DispatchError, DispatchResult, }; use sp_std::{vec, vec::Vec}; - use zeitgeist_primitives::types::OutcomeReport; + use zeitgeist_primitives::types::{Asset, Market, OutcomeReport}; use zrml_market_commons::MarketCommonsPalletApi; + pub(crate) type MarketOf = Market< + ::AccountId, + MarketCommonsBalanceOf, + ::BlockNumber, + MomentOf, + Asset>, + >; pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; pub(crate) type MarketCommonsBalanceOf = @@ -832,12 +839,7 @@ mod pallet { } #[cfg(any(feature = "runtime-benchmarks", test))] -pub(crate) fn market_mock() -> zeitgeist_primitives::types::Market< - T::AccountId, - MarketCommonsBalanceOf, - T::BlockNumber, - MomentOf, -> +pub(crate) fn market_mock() -> MarketOf where T: crate::Config, { @@ -846,6 +848,7 @@ where use zeitgeist_primitives::types::ScoringRule; zeitgeist_primitives::types::Market { + base_asset: zeitgeist_primitives::types::Asset::Ztg, creation: zeitgeist_primitives::types::MarketCreation::Permissionless, creator_fee: 0, creator: T::GlobalDisputesPalletId::get().into_account_truncating(), diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index da47638bb..aa1b61e3c 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1362,13 +1362,8 @@ mod pallet { ); // add report outcome to voting choices -<<<<<<< HEAD - if let Some(report) = market.report { - T::GlobalDisputes::push_vote_outcome( -======= if let Some(report) = &market.report { - T::GlobalDisputes::push_voting_outcome( ->>>>>>> main + T::GlobalDisputes::push_vote_outcome( &market_id, report.outcome.clone(), &report.by, @@ -1386,12 +1381,8 @@ mod pallet { )?; } -<<<<<<< HEAD T::GlobalDisputes::start_global_dispute(&market_id)?; -======= - // TODO(#372): Allow court with global disputes. ->>>>>>> main // ensure, that global disputes controls the resolution now // it does not end after the dispute period now, but after the global dispute end @@ -1698,16 +1689,12 @@ mod pallet { OracleDurationGreaterThanMaxOracleDuration, /// The weights length has to be equal to the assets length. WeightsLenMustEqualAssetsLen, - /// The start of the global dispute for this market happened already. -<<<<<<< HEAD - GlobalDisputeExistsAlready, -======= - GlobalDisputeAlreadyStarted, /// Provided base_asset is not allowed to be used as base_asset. InvalidBaseAsset, /// A foreign asset in not registered in AssetRegistry. UnregisteredForeignAsset, ->>>>>>> main + /// The start of the global dispute for this market happened already. + GlobalDisputeExistsAlready, } #[pallet::event] From 619d32deb1e3ae40ca19de1995935a7ca391119a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 23 Jan 2023 09:25:00 +0100 Subject: [PATCH 037/368] correct gd weights --- zrml/global-disputes/src/weights.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zrml/global-disputes/src/weights.rs b/zrml/global-disputes/src/weights.rs index 56db114c1..de47d25e6 100644 --- a/zrml/global-disputes/src/weights.rs +++ b/zrml/global-disputes/src/weights.rs @@ -133,12 +133,12 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:0) // Storage: GlobalDisputes Outcomes (r:3 w:2) fn refund_vote_fees(k: u32, _o: u32) -> Weight { - (31_076_000 as Weight) + Weight::from_ref_time(31_076_000) // Standard Error: 4_000 - .saturating_add((13_543_000 as Weight).saturating_mul(k as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) + .saturating_add(Weight::from_ref_time(13_543_000).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) } } From 44aa4b8cbfa7a3b57dffe300fd6802791859fb80 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 23 Jan 2023 09:51:14 +0100 Subject: [PATCH 038/368] update executive --- runtime/common/src/lib.rs | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 8e6b80663..3fba5e994 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -51,7 +51,7 @@ macro_rules! decl_common_types { type Address = sp_runtime::MultiAddress; - #[cfg(all(feature = "parachain", feature = "with-global-disputes"))] + #[cfg(feature = "parachain")] pub type Executive = frame_executive::Executive< Runtime, Block, @@ -61,7 +61,7 @@ macro_rules! decl_common_types { zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, >; - #[cfg(all(not(feature = "parachain"), feature = "with-global-disputes"))] + #[cfg(not(feature = "parachain"))] pub type Executive = frame_executive::Executive< Runtime, Block, @@ -71,33 +71,6 @@ macro_rules! decl_common_types { zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, >; - #[cfg(all(feature = "parachain", not(feature = "with-global-disputes")))] - pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, - ( - pallet_parachain_staking::migrations::MigrateAtStakeAutoCompound, - zrml_prediction_markets::migrations::UpdateMarketsForBaseAssetAndRecordBonds, - zrml_prediction_markets::migrations::AddFieldToAuthorityReport, - ), - >; - - #[cfg(all(not(feature = "parachain"), not(feature = "with-global-disputes")))] - pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, - ( - zrml_prediction_markets::migrations::UpdateMarketsForBaseAssetAndRecordBonds, - zrml_prediction_markets::migrations::AddFieldToAuthorityReport, - ), - >; - pub type Header = generic::Header; pub(crate) type NodeBlock = generic::Block; type RikiddoSigmoidFeeMarketVolumeEma = zrml_rikiddo::Instance1; From e2afe77f8131e09145760dafed146abc39a59328 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 23 Jan 2023 14:44:51 +0100 Subject: [PATCH 039/368] update frame executive migrations --- runtime/common/src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 3fba5e994..18cc6848c 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -51,6 +51,12 @@ macro_rules! decl_common_types { type Address = sp_runtime::MultiAddress; + #[cfg(feature = "with-global-disputes")] + type ConditionalMigration = zrml_global_disputes::migrations::ModifyGlobalDisputesStructures; + + #[cfg(not(feature = "with-global-disputes"))] + type ConditionalMigration = (); + #[cfg(feature = "parachain")] pub type Executive = frame_executive::Executive< Runtime, @@ -58,7 +64,7 @@ macro_rules! decl_common_types { frame_system::ChainContext, Runtime, AllPalletsWithSystem, - zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, + ConditionalMigration, >; #[cfg(not(feature = "parachain"))] @@ -68,7 +74,7 @@ macro_rules! decl_common_types { frame_system::ChainContext, Runtime, AllPalletsWithSystem, - zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, + ConditionalMigration, >; pub type Header = generic::Header; From ee2681a73de73be15a4914b7ba044c5527df747b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 08:52:31 +0100 Subject: [PATCH 040/368] fix weights file --- zrml/prediction-markets/src/weights.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/prediction-markets/src/weights.rs b/zrml/prediction-markets/src/weights.rs index c47450636..76a2a0110 100644 --- a/zrml/prediction-markets/src/weights.rs +++ b/zrml/prediction-markets/src/weights.rs @@ -106,7 +106,6 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(10)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(d.into()))) } // Storage: MarketCommons Markets (r:1 w:1) // Storage: Balances Reserves (r:1 w:1) From 7e0d2b352ecceb5bbcea51746f5ec863b0530985 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 09:38:26 +0100 Subject: [PATCH 041/368] apply Vivek's review comment --- Makefile | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 49f133974..77d44b7ed 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ check-dummy: RUST_LOG=runtime=trace,try-runtime::cli=trace,executor=trace \ cargo run \ --bin=zeitgeist \ - --features=parachain,try-runtime \ + --features=parachain,try-runtime${ADDITIONAL_FEATURES} \ try-runtime \ --execution=Native \ --chain=${TRYRUNTIME_CHAIN} \ @@ -29,25 +29,10 @@ check-dummy: --uri=${TRYRUNTIME_URL} try-runtime-upgrade-battery-station: - @$(MAKE) TRYRUNTIME_CHAIN="battery_station_staging" TRYRUNTIME_URL="wss://bsr.zeitgeist.pm:443" -- --try-runtime + @$(MAKE) TRYRUNTIME_CHAIN="battery_station_staging" TRYRUNTIME_URL="wss://bsr.zeitgeist.pm:443" ADDITIONAL_FEATURES=",with-global-disputes" -- --try-runtime try-runtime-upgrade-zeitgeist: - @$(MAKE) TRYRUNTIME_CHAIN="zeitgeist_staging" TRYRUNTIME_URL="wss://zeitgeist-rpc.dwellir.com:443" -- --try-runtime - ---try-runtime-with-global-disputes: - RUST_LOG=runtime=trace,try-runtime::cli=trace,executor=trace \ - cargo run \ - --bin=zeitgeist \ - --features=parachain,try-runtime,with-global-disputes \ - try-runtime \ - --execution=Native \ - --chain=${TRYRUNTIME_CHAIN} \ - on-runtime-upgrade \ - live \ - --uri=${TRYRUNTIME_URL} - -try-runtime-upgrade-battery-station-with-global-disputes: - @$(MAKE) TRYRUNTIME_CHAIN="battery_station_staging" TRYRUNTIME_URL="wss://bsr.zeitgeist.pm:443" -- --try-runtime-with-global-disputes + @$(MAKE) TRYRUNTIME_CHAIN="zeitgeist_staging" TRYRUNTIME_URL="wss://zeitgeist-rpc.dwellir.com:443" ADDITIONAL_FEATURES="" -- --try-runtime build: SKIP_WASM_BUILD= cargo build From 6b2f233ae7fe3012951c880b66b718f7f7c7dea9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 09:39:52 +0100 Subject: [PATCH 042/368] update push_vote_outcome doc comment --- zrml/global-disputes/src/global_disputes_pallet_api.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 1de975afd..9ff743702 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -30,11 +30,6 @@ pub trait GlobalDisputesPalletApi { /// - `outcome` - The voting outcome to push. /// - `owner` - The owner of the outcome. /// - `initial_vote_balance` - The initial vote amount for the specified outcome. - /// - /// # Returns - /// - /// Returns the dispute mechanism's report if available, otherwise `None`. If `None` is - /// returned, this means that the dispute could not be resolved. fn push_vote_outcome( market_id: &MarketId, outcome: OutcomeReport, From 6e53282b3d9e3ee8ef73cc13f8645de20811897b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 13:38:21 +0100 Subject: [PATCH 043/368] apply review suggestion --- zrml/global-disputes/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 2499bfc97..6df3286a1 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -704,13 +704,12 @@ mod pallet { reward_info: RewardInfoOf, owner: AccountIdOf, ) -> DispatchResultWithPostInfo { - let res = T::Currency::transfer( + T::Currency::transfer( &reward_info.source, &owner, reward_info.reward, ExistenceRequirement::AllowDeath, - ); - debug_assert!(res.is_ok(), "Global Disputes: Rewarding outcome owner failed."); + )?; Self::deposit_event(Event::OutcomeOwnerRewarded { market_id: reward_info.market_id, owner, From 6ea85bc41839b7ab731b50840637bb42b5e99522 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 14:04:36 +0100 Subject: [PATCH 044/368] add test for purge_outcomes --- zrml/global-disputes/src/tests.rs | 41 +++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 6b706ca83..3d6d29285 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -32,7 +32,9 @@ use frame_support::{ use pallet_balances::{BalanceLock, Error as BalancesError}; use sp_runtime::traits::Zero; use zeitgeist_primitives::{ - constants::mock::{GlobalDisputeLockId, MinOutcomeVoteAmount, VotingOutcomeFee, BASE}, + constants::mock::{ + GlobalDisputeLockId, MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, BASE, + }, types::OutcomeReport, }; use zrml_market_commons::{Error as MarketError, Markets}; @@ -846,7 +848,7 @@ fn determine_voting_winner_works_with_accumulated_votes_for_alice() { } #[test] -fn purge_outcomes_works() { +fn purge_outcomes_fully_cleaned_works() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); @@ -886,6 +888,41 @@ fn purge_outcomes_works() { }); } +#[test] +fn purge_outcomes_partially_cleaned_works() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + for i in 0..(2 * RemoveKeysLimit::get()) { + GlobalDisputes::push_vote_outcome( + &market_id, + OutcomeReport::Scalar(i.into()), + &ALICE, + SETUP_AMOUNT, + ) + .unwrap(); + } + + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + + assert!(GlobalDisputes::determine_voting_winner(&market_id).is_some()); + + assert_ok!(GlobalDisputes::purge_outcomes(Origin::signed(ALICE), market_id,)); + + System::assert_last_event(Event::::OutcomesPartiallyCleaned { market_id }.into()); + + assert!(>::iter_prefix(market_id).next().is_some()); + + assert_ok!(GlobalDisputes::purge_outcomes(Origin::signed(ALICE), market_id,)); + + System::assert_last_event(Event::::OutcomesFullyCleaned { market_id }.into()); + + assert_eq!(>::iter_prefix(market_id).next(), None); + }); +} + #[test] fn unlock_clears_lock_info() { ExtBuilder::default().build().execute_with(|| { From 1a2c491f7290efde9c3a30a99eea8dbb1f2d876a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 14:27:24 +0100 Subject: [PATCH 045/368] add refund_vote_fees test --- zrml/global-disputes/src/tests.rs | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 3d6d29285..6c33bad84 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -21,6 +21,7 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, market_mock, mock::*, + BalanceOf, types::{GDInfo, GDStatus, OutcomeInfo, Possession}, Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, }; @@ -923,6 +924,69 @@ fn purge_outcomes_partially_cleaned_works() { }); } +#[test] +fn refund_vote_fees_works() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let pushed_outcome_1 = 0; + GlobalDisputes::push_vote_outcome( + &market_id, + OutcomeReport::Scalar(pushed_outcome_1), + &ALICE, + SETUP_AMOUNT, + ) + .unwrap(); + + let pushed_outcome_2 = 20; + GlobalDisputes::push_vote_outcome( + &market_id, + OutcomeReport::Scalar(pushed_outcome_2), + &ALICE, + SETUP_AMOUNT, + ) + .unwrap(); + + let offset = pushed_outcome_1.max(pushed_outcome_2) + 1; + + assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + + let mut overall_fees = >::zero(); + // minus 2 because of the above push_vote_outcome calls + for i in 0..(2 * RemoveKeysLimit::get() - 2) { + assert_ok!(GlobalDisputes::add_vote_outcome( + Origin::signed(ALICE), + market_id, + // offset to not conflict with pushed outcomes + OutcomeReport::Scalar((offset + i as u128).into()), + )); + overall_fees = overall_fees.saturating_add(VotingOutcomeFee::get()); + } + + assert_ok!(GlobalDisputes::destroy_global_dispute(&market_id)); + + let alice_free_balance_before = Balances::free_balance(&ALICE); + assert_ok!(GlobalDisputes::refund_vote_fees(Origin::signed(ALICE), market_id,)); + + System::assert_last_event(Event::::OutcomesPartiallyCleaned { market_id }.into()); + + assert!(>::iter_prefix(market_id).next().is_some()); + + assert_ok!(GlobalDisputes::refund_vote_fees(Origin::signed(ALICE), market_id,)); + + System::assert_last_event(Event::::OutcomesFullyCleaned { market_id }.into()); + + assert_eq!(>::iter_prefix(market_id).next(), None); + + assert_eq!( + Balances::free_balance(&ALICE), + alice_free_balance_before.saturating_add(overall_fees) + ); + }); +} + #[test] fn unlock_clears_lock_info() { ExtBuilder::default().build().execute_with(|| { From 4a387b4f606930d1783f6cf05de3c673edd5641e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 14:27:31 +0100 Subject: [PATCH 046/368] cargo fmt --- zrml/global-disputes/src/tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 6c33bad84..9cf150d51 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -21,9 +21,8 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, market_mock, mock::*, - BalanceOf, types::{GDInfo, GDStatus, OutcomeInfo, Possession}, - Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, + BalanceOf, Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, }; use frame_support::{ assert_noop, assert_ok, From 396f999d8defb98f1fa5f7dd3a8791b4f57b4f36 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 24 Jan 2023 16:30:23 +0100 Subject: [PATCH 047/368] fix clippy --- zrml/global-disputes/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 9cf150d51..f66672186 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -959,7 +959,7 @@ fn refund_vote_fees_works() { Origin::signed(ALICE), market_id, // offset to not conflict with pushed outcomes - OutcomeReport::Scalar((offset + i as u128).into()), + OutcomeReport::Scalar(offset + i as u128), )); overall_fees = overall_fees.saturating_add(VotingOutcomeFee::get()); } From 9de62178032e8be70a02b5ac407819608aa20164 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 25 Jan 2023 10:40:45 +0100 Subject: [PATCH 048/368] set predefined add vote outcome period --- primitives/src/constants/mock.rs | 3 +- runtime/battery-station/src/parameters.rs | 6 +- runtime/common/src/lib.rs | 5 +- runtime/zeitgeist/src/parameters.rs | 6 +- zrml/global-disputes/src/benchmarks.rs | 24 ++++++- .../src/global_disputes_pallet_api.rs | 4 +- zrml/global-disputes/src/lib.rs | 72 ++++++++++++++++--- zrml/global-disputes/src/mock.rs | 67 +++++++++++++++-- zrml/global-disputes/src/tests.rs | 37 ++++++++++ zrml/global-disputes/src/types.rs | 16 +++-- zrml/prediction-markets/src/benchmarks.rs | 7 +- zrml/prediction-markets/src/lib.rs | 20 ++---- zrml/prediction-markets/src/mock.rs | 9 +-- zrml/prediction-markets/src/tests.rs | 7 +- 14 files changed, 223 insertions(+), 60 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 1436e58a4..eecb6a038 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -23,12 +23,14 @@ parameter_types! { // Global disputes parameters parameter_types! { + pub const AddOutcomePeriod: BlockNumber = 20; pub const GlobalDisputeLockId: LockIdentifier = *b"zge/vote"; pub const GlobalDisputesPalletId: PalletId = PalletId(*b"zge/gldp"); pub const MaxGlobalDisputeVotes: u32 = 50; pub const MaxOwners: u32 = 10; pub const MinOutcomeVoteAmount: Balance = 10 * CENT; pub const RemoveKeysLimit: u32 = 250; + pub const VotePeriod: BlockNumber = 140; pub const VotingOutcomeFee: Balance = 100 * CENT; } @@ -42,7 +44,6 @@ parameter_types! { pub const AdvisoryBond: Balance = 25 * CENT; pub const DisputeBond: Balance = 5 * BASE; pub const DisputeFactor: Balance = 2 * BASE; - pub const GlobalDisputePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; pub const MaxCategories: u16 = 10; pub const MaxDisputeDuration: BlockNumber = 50; pub const MaxDisputes: u16 = 6; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 9eb46b3fb..0f5e7a01c 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -353,18 +353,20 @@ parameter_types! { #[cfg(feature = "with-global-disputes")] parameter_types! { // Global Disputes + /// The time period in which the addition of new outcomes are allowed. + pub const AddOutcomePeriod: BlockNumber = BLOCKS_PER_DAY; /// Vote lock identifier, mainly used for the LockableCurrency on the native token. pub const GlobalDisputeLockId: LockIdentifier = GLOBAL_DISPUTES_LOCK_ID; /// Pallet identifier pub const GlobalDisputesPalletId: PalletId = GLOBAL_DISPUTES_PALLET_ID; - /// The period for a global dispute to end. - pub const GlobalDisputePeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The maximum number of owners for a voting outcome for private API calls of `push_vote_outcome`. pub const MaxOwners: u32 = 10; /// The maximum number of market ids (participate in multiple different global disputes at the same time) for one account to vote on outcomes. pub const MaxGlobalDisputeVotes: u32 = 50; /// The minimum required amount to vote on an outcome. pub const MinOutcomeVoteAmount: Balance = 10 * BASE; + /// The time period in which votes are allowed. + pub const VotePeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The fee required to add a voting outcome. pub const VotingOutcomeFee: Balance = 200 * BASE; /// The remove limit for the Outcomes storage double map. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 40de12031..316f182dc 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1020,8 +1020,6 @@ macro_rules! impl_config_traits { type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; - #[cfg(feature = "with-global-disputes")] - type GlobalDisputePeriod = GlobalDisputePeriod; // LiquidityMining is currently unstable. // NoopLiquidityMining will be applied only to mainnet once runtimes are separated. type LiquidityMining = NoopLiquidityMining; @@ -1081,7 +1079,9 @@ macro_rules! impl_config_traits { #[cfg(feature = "with-global-disputes")] impl zrml_global_disputes::Config for Runtime { + type AddOutcomePeriod = AddOutcomePeriod; type Currency = Balances; + type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type GlobalDisputeLockId = GlobalDisputeLockId; type GlobalDisputesPalletId = GlobalDisputesPalletId; @@ -1090,6 +1090,7 @@ macro_rules! impl_config_traits { type MaxOwners = MaxOwners; type MinOutcomeVoteAmount = MinOutcomeVoteAmount; type RemoveKeysLimit = RemoveKeysLimit; + type VotePeriod = VotePeriod; type VotingOutcomeFee = VotingOutcomeFee; type WeightInfo = zrml_global_disputes::weights::WeightInfo; } diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index c7ee4aa77..e0c822aab 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -353,18 +353,20 @@ parameter_types! { #[cfg(feature = "with-global-disputes")] parameter_types! { // Global Disputes + /// The time period in which the addition of new outcomes are allowed. + pub const AddOutcomePeriod: BlockNumber = BLOCKS_PER_DAY; /// Vote lock identifier, mainly used for the LockableCurrency on the native token. pub const GlobalDisputeLockId: LockIdentifier = GLOBAL_DISPUTES_LOCK_ID; /// Pallet identifier pub const GlobalDisputesPalletId: PalletId = GLOBAL_DISPUTES_PALLET_ID; - /// The period for a global dispute to end. - pub const GlobalDisputePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The maximum number of owners for a voting outcome for private API calls of `push_vote_outcome`. pub const MaxOwners: u32 = 10; /// The maximum number of market ids (participate in multiple different global disputes at the same time) for one account to vote on outcomes. pub const MaxGlobalDisputeVotes: u32 = 50; /// The minimum required amount to vote on an outcome. pub const MinOutcomeVoteAmount: Balance = 10 * BASE; + /// The time period in which votes are allowed. + pub const VotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The fee required to add a voting outcome. pub const VotingOutcomeFee: Balance = 200 * BASE; /// The remove limit for the Outcomes storage double map. diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index 064ae240a..ad94e4793 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -100,8 +100,16 @@ benchmarks! { let vote_sum = amount - 1u128.saturated_into(); let possession = Some(Possession::Shared { owners: Default::default() }); let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; - let gd_info = GDInfo {winner_outcome: outcome.clone(), status: GDStatus::Active, outcome_info}; + let now = >::block_number(); + let add_outcome_end = now + T::AddOutcomePeriod::get(); + let vote_end = add_outcome_end + T::VotePeriod::get(); + let gd_info = GDInfo { + winner_outcome: outcome.clone(), + status: GDStatus::Active { add_outcome_end, vote_end }, + outcome_info, + }; >::insert(market_id, gd_info); + >::set_block_number(add_outcome_end + 1u32.into()); }: _(RawOrigin::Signed(caller.clone()), market_id, outcome.clone(), amount) verify { assert_last_event::( @@ -131,7 +139,14 @@ benchmarks! { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; // is_finished is false, // because we need `lock_needed` to be greater zero to set a lock. - let gd_info = GDInfo {winner_outcome: outcome, status: GDStatus::Active, outcome_info}; + let now = >::block_number(); + let add_outcome_end = now + T::AddOutcomePeriod::get(); + let vote_end = add_outcome_end + T::VotePeriod::get(); + let gd_info = GDInfo { + winner_outcome: outcome, + status: GDStatus::Active { add_outcome_end, vote_end }, + outcome_info + }; let caller: T::AccountId = whitelisted_caller(); let voter: T::AccountId = account("voter", 0, 0); @@ -217,9 +232,12 @@ benchmarks! { .unwrap(); let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession: None }; + let now = >::block_number(); + let add_outcome_end = now + T::AddOutcomePeriod::get(); + let vote_end = add_outcome_end + T::VotePeriod::get(); let gd_info = GDInfo { winner_outcome: OutcomeReport::Scalar(0), - status: GDStatus::Active, + status: GDStatus::Active { add_outcome_end, vote_end }, outcome_info, }; diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 9ff743702..fd78135fe 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -22,7 +22,7 @@ use sp_runtime::{DispatchError, DispatchResult}; use zeitgeist_primitives::types::OutcomeReport; /// The trait to initiate and resolve the global disputes. -pub trait GlobalDisputesPalletApi { +pub trait GlobalDisputesPalletApi { /// Push a voting outcome for one global dispute. /// /// # Arguments @@ -88,7 +88,7 @@ pub trait GlobalDisputesPalletApi { /// /// # Arguments /// - `market_id` - The id of the market. - fn start_global_dispute(market_id: &MarketId) -> Result<(), DispatchError>; + fn start_global_dispute(market_id: &MarketId) -> Result; /// Destroy a global dispute and allow to return all funds of the participants. /// diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 6df3286a1..a5c91bd8a 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -51,7 +51,10 @@ mod pallet { DispatchError, DispatchResult, }; use sp_std::{vec, vec::Vec}; - use zeitgeist_primitives::types::{Asset, Market, OutcomeReport}; + use zeitgeist_primitives::{ + traits::DisputeResolutionApi, + types::{Asset, Market, OutcomeReport}, + }; use zrml_market_commons::MarketCommonsPalletApi; pub(crate) type MarketOf = Market< @@ -73,7 +76,12 @@ mod pallet { pub type AccountIdOf = ::AccountId; pub type OutcomeInfoOf = OutcomeInfo, BalanceOf, OwnerInfoOf>; - pub type GDInfoOf = GDInfo, BalanceOf, OwnerInfoOf>; + pub type GDInfoOf = GDInfo< + AccountIdOf, + BalanceOf, + OwnerInfoOf, + ::BlockNumber, + >; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub(crate) type OwnerInfoOf = BoundedVec, ::MaxOwners>; pub type LockInfoOf = @@ -88,11 +96,22 @@ mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The time period in which the addition of new outcomes are allowed. + #[pallet::constant] + type AddOutcomePeriod: Get; + /// The currency implementation used to lock tokens for voting. type Currency: LockableCurrency; type Event: From> + IsType<::Event>; + type DisputeResolution: DisputeResolutionApi< + AccountId = Self::AccountId, + BlockNumber = Self::BlockNumber, + MarketId = MarketIdOf, + Moment = MomentOf, + >; + /// The vote lock identifier. #[pallet::constant] type GlobalDisputeLockId: Get; @@ -127,6 +146,10 @@ mod pallet { #[pallet::constant] type RemoveKeysLimit: Get; + /// The time period in which votes are allowed. + #[pallet::constant] + type VotePeriod: Get; + /// The fee required to add a voting outcome. #[pallet::constant] type VotingOutcomeFee: Get>; @@ -234,6 +257,10 @@ mod pallet { SharedPossessionRequired, /// The global dispute period is not over yet. The winner is not yet determined. UnfinishedGlobalDispute, + /// The period in which outcomes can be added is over. + AddOutcomePeriodIsOver, + /// It is not inside the period in which votes are allowed. + NotInVotePeriod, } #[pallet::call] @@ -265,7 +292,12 @@ mod pallet { let gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; - ensure!(gd_info.status == GDStatus::Active, Error::::InvalidGlobalDisputeStatus); + let now = >::block_number(); + if let GDStatus::Active { add_outcome_end, vote_end: _ } = gd_info.status { + ensure!(now <= add_outcome_end, Error::::AddOutcomePeriodIsOver); + } else { + return Err(Error::::InvalidGlobalDisputeStatus.into()); + } ensure!( !>::contains_key(market_id, &outcome), @@ -493,7 +525,12 @@ mod pallet { let gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; - ensure!(gd_info.status == GDStatus::Active, Error::::InvalidGlobalDisputeStatus); + let now = >::block_number(); + if let GDStatus::Active { add_outcome_end, vote_end } = gd_info.status { + ensure!(add_outcome_end < now && now <= vote_end, Error::::NotInVotePeriod); + } else { + return Err(Error::::InvalidGlobalDisputeStatus.into()); + } let mut outcome_info = >::get(market_id, &outcome).ok_or(Error::::OutcomeDoesNotExist)?; @@ -718,7 +755,8 @@ mod pallet { } } - impl GlobalDisputesPalletApi, AccountIdOf, BalanceOf> for Pallet + impl GlobalDisputesPalletApi, AccountIdOf, BalanceOf, T::BlockNumber> + for Pallet where T: Config, { @@ -801,13 +839,15 @@ mod pallet { fn is_unfinished(market_id: &MarketIdOf) -> bool { if let Some(gd_info) = >::get(market_id) { - return gd_info.status == GDStatus::Active - || gd_info.status == GDStatus::Initialized; + return matches!( + gd_info.status, + GDStatus::Active { add_outcome_end: _, vote_end: _ } | GDStatus::Initialized + ); } false } - fn start_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { + fn start_global_dispute(market_id: &MarketIdOf) -> Result { T::MarketCommons::market(market_id)?; let mut iter = >::iter_prefix(market_id); @@ -816,13 +856,20 @@ mod pallet { Error::::AtLeastTwoOutcomesRequired ); - >::try_mutate(market_id, |gd_info| { + let now = >::block_number(); + let add_outcome_end = now.saturating_add(T::AddOutcomePeriod::get()); + let vote_end = add_outcome_end.saturating_add(T::VotePeriod::get()); + let ids_len = T::DisputeResolution::add_auto_resolve(market_id, vote_end)?; + + >::try_mutate(market_id, |gd_info| -> DispatchResult { let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; - raw_gd_info.status = GDStatus::Active; + raw_gd_info.status = GDStatus::Active { add_outcome_end, vote_end }; *gd_info = Some(raw_gd_info.clone()); Ok(()) - }) + })?; + + Ok(ids_len) } fn destroy_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { @@ -830,6 +877,9 @@ mod pallet { let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; raw_gd_info.status = GDStatus::Destroyed; + if let GDStatus::Active { add_outcome_end: _, vote_end } = raw_gd_info.status { + T::DisputeResolution::remove_auto_resolve(market_id, vote_end); + } *gd_info = Some(raw_gd_info.clone()); Ok(()) }) diff --git a/zrml/global-disputes/src/mock.rs b/zrml/global-disputes/src/mock.rs index 94c87681d..0048ab4f1 100644 --- a/zrml/global-disputes/src/mock.rs +++ b/zrml/global-disputes/src/mock.rs @@ -18,19 +18,27 @@ #![cfg(test)] use crate::{self as zrml_global_disputes}; -use frame_support::{construct_runtime, parameter_types, traits::Everything}; +use frame_support::{ + construct_runtime, + pallet_prelude::{DispatchError, Weight}, + parameter_types, + traits::Everything, + BoundedVec, +}; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, GlobalDisputeLockId, GlobalDisputesPalletId, MaxReserves, - MinOutcomeVoteAmount, MinimumPeriod, PmPalletId, RemoveKeysLimit, VotingOutcomeFee, BASE, + AddOutcomePeriod, BlockHashCount, GlobalDisputeLockId, GlobalDisputesPalletId, MaxReserves, + MinOutcomeVoteAmount, MinimumPeriod, PmPalletId, RemoveKeysLimit, VotePeriod, + VotingOutcomeFee, BASE, }, + traits::DisputeResolutionApi, types::{ - AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment, - UncheckedExtrinsicTest, + AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketDispute, + MarketId, Moment, UncheckedExtrinsicTest, }, }; @@ -56,13 +64,61 @@ construct_runtime!( } ); +// NoopResolution implements DisputeResolutionApi with no-ops. +pub struct NoopResolution; + +impl DisputeResolutionApi for NoopResolution { + type AccountId = AccountIdTest; + type Balance = Balance; + type BlockNumber = BlockNumber; + type MarketId = MarketId; + type MaxDisputes = u32; + type Moment = Moment; + + fn resolve( + _market_id: &Self::MarketId, + _market: &Market< + Self::AccountId, + Self::Balance, + Self::BlockNumber, + Self::Moment, + Asset, + >, + ) -> Result { + Ok(Weight::zero()) + } + + fn add_auto_resolve( + _market_id: &Self::MarketId, + _resolve_at: Self::BlockNumber, + ) -> Result { + Ok(0u32) + } + + fn auto_resolve_exists(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> bool { + false + } + + fn remove_auto_resolve(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> u32 { + 0u32 + } + + fn get_disputes( + _market_id: &Self::MarketId, + ) -> BoundedVec, Self::MaxDisputes> { + Default::default() + } +} + parameter_types! { pub const MaxGlobalDisputeVotes: u32 = 50; pub const MaxOwners: u32 = 10; } impl crate::Config for Runtime { + type AddOutcomePeriod = AddOutcomePeriod; type Currency = Balances; + type DisputeResolution = NoopResolution; type Event = Event; type GlobalDisputeLockId = GlobalDisputeLockId; type GlobalDisputesPalletId = GlobalDisputesPalletId; @@ -71,6 +127,7 @@ impl crate::Config for Runtime { type MaxOwners = MaxOwners; type MinOutcomeVoteAmount = MinOutcomeVoteAmount; type RemoveKeysLimit = RemoveKeysLimit; + type VotePeriod = VotePeriod; type VotingOutcomeFee = VotingOutcomeFee; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index f66672186..053424db4 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -58,6 +58,13 @@ fn setup_vote_outcomes_with_hundred(market_id: &MarketIdOf) { .unwrap(); } +fn set_vote_period() { + let now = >::block_number(); + >::set_block_number( + now + ::AddOutcomePeriod::get() + 1, + ); +} + fn check_outcome_sum( market_id: &MarketIdOf, outcome: OutcomeReport, @@ -437,6 +444,8 @@ fn determine_voting_winner_sets_the_last_outcome_for_same_vote_balances_as_the_c setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), market_id, @@ -487,6 +496,8 @@ fn vote_on_outcome_check_event() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(EVE), market_id, @@ -542,6 +553,8 @@ fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { GlobalDisputes::start_global_dispute(&market_id).unwrap(); + set_vote_period(); + assert_noop!( GlobalDisputes::vote_on_outcome( Origin::signed(*disputor), @@ -579,6 +592,8 @@ fn transfer_fails_with_fully_locked_balance() { assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + let disputor = &ALICE; let free_balance_disputor_before = Balances::free_balance(disputor); let arbitrary_amount = 42 * BASE; @@ -614,6 +629,8 @@ fn reserve_fails_with_fully_locked_balance() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + let disputor = &ALICE; let free_balance_disputor_before = Balances::free_balance(disputor); let arbitrary_amount = 42 * BASE; @@ -649,6 +666,8 @@ fn determine_voting_winner_works_four_outcome_votes() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), market_id, @@ -702,6 +721,8 @@ fn determine_voting_winner_works_three_outcome_votes() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), market_id, @@ -749,6 +770,8 @@ fn determine_voting_winner_works_two_outcome_votes() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), market_id, @@ -796,6 +819,8 @@ fn determine_voting_winner_works_with_accumulated_votes_for_alice() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), market_id, @@ -857,6 +882,8 @@ fn purge_outcomes_fully_cleaned_works() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), market_id, @@ -996,6 +1023,8 @@ fn unlock_clears_lock_info() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_ok!(GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), market_id, @@ -1031,6 +1060,8 @@ fn vote_fails_if_outcome_does_not_exist() { assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_noop!( GlobalDisputes::vote_on_outcome( Origin::signed(ALICE), @@ -1053,6 +1084,8 @@ fn locking_works_for_one_market() { setup_vote_outcomes_with_hundred(&market_id); assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + set_vote_period(); + assert_eq!(>::get(ALICE), vec![]); assert!(Balances::locks(ALICE).is_empty()); assert_eq!(>::get(BOB), vec![]); @@ -1148,6 +1181,8 @@ fn locking_works_for_two_markets_with_stronger_first_unlock() { assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1)); assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2)); + set_vote_period(); + assert_eq!(>::get(ALICE), vec![]); assert!(Balances::locks(ALICE).is_empty()); assert_eq!(>::get(BOB), vec![]); @@ -1245,6 +1280,8 @@ fn locking_works_for_two_markets_with_weaker_first_unlock() { assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1)); assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2)); + set_vote_period(); + assert_eq!(>::get(ALICE), vec![]); assert!(Balances::locks(ALICE).is_empty()); assert_eq!(>::get(BOB), vec![]); diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 3f75489d1..3c505aef0 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -36,16 +36,18 @@ pub struct OutcomeInfo { /// The general information about the global dispute. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct GDInfo { +pub struct GDInfo { /// The outcome, which is in the lead. pub winner_outcome: OutcomeReport, /// The information about the winning outcome. pub outcome_info: OutcomeInfo, /// The current status of the global dispute. - pub status: GDStatus, + pub status: GDStatus, } -impl GDInfo { +impl + GDInfo +{ pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession: None }; GDInfo { winner_outcome: outcome, status: GDStatus::Initialized, outcome_info } @@ -58,11 +60,13 @@ impl GDInfo { /// The global dispute is initialized. Initialized, - /// The global dispute is in progress. - Active, + /// The global dispute is in progress. Save the addition of outcome end and vote end. + /// The block number `add_outcome_end`, when the addition of new outcomes is over. + /// The block number `vote_end`, when the global dispute voting period is over. + Active { add_outcome_end: BlockNumber, vote_end: BlockNumber }, /// The global dispute is finished. Finished, /// The global dispute was triggered to get destroyed. diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index f6e0cebe4..1867297c6 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -223,7 +223,7 @@ fn setup_reported_categorical_market_with_pool::MarketCommons as MarketCommonsPalletApi>::MarketId: From<::MarketId>, } @@ -833,10 +833,11 @@ benchmarks! { #[cfg(feature = "with-global-disputes")] { - let global_dispute_end = current_block + T::GlobalDisputePeriod::get(); + let add_outcome_end = current_block + ::AddOutcomePeriod::get(); + let vote_end = add_outcome_end + ::VotePeriod::get(); // the complexity depends on MarketIdsPerDisputeBlock at the current block // this is because a variable number of market ids need to be decoded from the storage - MarketIdsPerDisputeBlock::::insert(global_dispute_end, market_ids_1); + MarketIdsPerDisputeBlock::::insert(vote_end, market_ids_1); } let call = Call::::start_global_dispute { market_id }; diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 2578769a1..71b4aa1bc 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1382,27 +1382,18 @@ mod pallet { )?; } - T::GlobalDisputes::start_global_dispute(&market_id)?; - // ensure, that global disputes controls the resolution now // it does not end after the dispute period now, but after the global dispute end // ignore first of tuple because we always have max disputes let (_, ids_len_2) = Self::clear_auto_resolve(&market_id)?; - let now = >::block_number(); - let global_dispute_end = now.saturating_add(T::GlobalDisputePeriod::get()); - let market_ids_len = >::try_mutate( - global_dispute_end, - |ids| -> Result { - ids.try_push(market_id).map_err(|_| >::StorageOverflow)?; - Ok(ids.len() as u32) - }, - )?; + // global disputes uses DisputeResolution API to control its resolution + let ids_len_1 = T::GlobalDisputes::start_global_dispute(&market_id)?; Self::deposit_event(Event::GlobalDisputeStarted(market_id)); - Ok(Some(T::WeightInfo::start_global_dispute(market_ids_len, ids_len_2)).into()) + Ok(Some(T::WeightInfo::start_global_dispute(ids_len_1, ids_len_2)).into()) } #[cfg(not(feature = "with-global-disputes"))] @@ -1483,12 +1474,9 @@ mod pallet { MarketIdOf, Self::AccountId, BalanceOf, + Self::BlockNumber, >; - /// The number of blocks the global dispute period remains open. - #[cfg(feature = "with-global-disputes")] - type GlobalDisputePeriod: Get; - type LiquidityMining: LiquidityMiningPalletApi< AccountId = Self::AccountId, Balance = BalanceOf, diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 7c77a017a..ca59051d0 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -55,8 +55,8 @@ use zeitgeist_primitives::{ #[cfg(feature = "with-global-disputes")] use zeitgeist_primitives::constants::mock::{ - GlobalDisputeLockId, GlobalDisputePeriod, GlobalDisputesPalletId, MaxGlobalDisputeVotes, - MaxOwners, MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, + AddOutcomePeriod, GlobalDisputeLockId, GlobalDisputesPalletId, MaxGlobalDisputeVotes, + MaxOwners, MinOutcomeVoteAmount, RemoveKeysLimit, VotePeriod, VotingOutcomeFee, }; use zrml_rikiddo::types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}; @@ -151,8 +151,6 @@ impl crate::Config for Runtime { type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; - #[cfg(feature = "with-global-disputes")] - type GlobalDisputePeriod = GlobalDisputePeriod; type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; type MaxDisputes = MaxDisputes; @@ -325,7 +323,9 @@ impl zrml_simple_disputes::Config for Runtime { #[cfg(feature = "with-global-disputes")] impl zrml_global_disputes::Config for Runtime { + type AddOutcomePeriod = AddOutcomePeriod; type Event = Event; + type DisputeResolution = prediction_markets::Pallet; type MarketCommons = MarketCommons; type Currency = Balances; type GlobalDisputeLockId = GlobalDisputeLockId; @@ -334,6 +334,7 @@ impl zrml_global_disputes::Config for Runtime { type MaxOwners = MaxOwners; type MinOutcomeVoteAmount = MinOutcomeVoteAmount; type RemoveKeysLimit = RemoveKeysLimit; + type VotePeriod = VotePeriod; type VotingOutcomeFee = VotingOutcomeFee; type WeightInfo = zrml_global_disputes::weights::WeightInfo; } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 1587fadb0..e0ff9b340 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -2956,6 +2956,7 @@ fn start_global_dispute_works() { #[cfg(feature = "with-global-disputes")] { + use zeitgeist_primitives::constants::mock::{AddOutcomePeriod, VotePeriod}; use zrml_global_disputes::GlobalDisputesPalletApi; let now = >::block_number(); @@ -2981,9 +2982,9 @@ fn start_global_dispute_works() { let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); assert_eq!(removable_market_ids.len(), 0); - let market_ids = MarketIdsPerDisputeBlock::::get( - now + ::GlobalDisputePeriod::get(), - ); + let add_outcome_end = now + AddOutcomePeriod::get(); + let vote_end = add_outcome_end + VotePeriod::get(); + let market_ids = MarketIdsPerDisputeBlock::::get(vote_end); assert_eq!(market_ids, vec![market_id]); assert!(GlobalDisputes::is_unfinished(&market_id)); System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); From 4edf91dfc27000be9776220260c2e646cc09592a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 25 Jan 2023 10:57:09 +0100 Subject: [PATCH 049/368] use gd constants in standalone mode --- runtime/common/src/lib.rs | 4 ++++ zrml/prediction-markets/src/benchmarks.rs | 6 +++--- zrml/prediction-markets/src/lib.rs | 6 ++++++ zrml/prediction-markets/src/mock.rs | 4 ++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 316f182dc..94830c8f4 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1020,6 +1020,10 @@ macro_rules! impl_config_traits { type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; + #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] + type AddOutcomePeriod = AddOutcomePeriod; + #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] + type VotePeriod = VotePeriod; // LiquidityMining is currently unstable. // NoopLiquidityMining will be applied only to mainnet once runtimes are separated. type LiquidityMining = NoopLiquidityMining; diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 1867297c6..da80c9afb 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -223,7 +223,7 @@ fn setup_reported_categorical_market_with_pool::MarketCommons as MarketCommonsPalletApi>::MarketId: From<::MarketId>, } @@ -833,8 +833,8 @@ benchmarks! { #[cfg(feature = "with-global-disputes")] { - let add_outcome_end = current_block + ::AddOutcomePeriod::get(); - let vote_end = add_outcome_end + ::VotePeriod::get(); + let add_outcome_end = current_block + ::AddOutcomePeriod::get(); + let vote_end = add_outcome_end + ::VotePeriod::get(); // the complexity depends on MarketIdsPerDisputeBlock at the current block // this is because a variable number of market ids need to be decoded from the storage MarketIdsPerDisputeBlock::::insert(vote_end, market_ids_1); diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 71b4aa1bc..5125b8bef 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1477,6 +1477,12 @@ mod pallet { Self::BlockNumber, >; + #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] + type AddOutcomePeriod: Get; + + #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] + type VotePeriod: Get; + type LiquidityMining: LiquidityMiningPalletApi< AccountId = Self::AccountId, Balance = BalanceOf, diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index ca59051d0..26afb145e 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -151,6 +151,10 @@ impl crate::Config for Runtime { type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; + #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] + type AddOutcomePeriod = AddOutcomePeriod; + #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] + type VotePeriod = VotePeriod; type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; type MaxDisputes = MaxDisputes; From 06314bbc92909241477267a4e0f698412e658979 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 31 Jan 2023 12:19:30 +0100 Subject: [PATCH 050/368] fix after merge --- Cargo.lock | 2 +- runtime/common/src/lib.rs | 10 +- zrml/prediction-markets/src/lib.rs | 27 +-- zrml/prediction-markets/src/migrations.rs | 197 ++++++++++++---------- zrml/prediction-markets/src/tests.rs | 26 ++- 5 files changed, 145 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22e66ec78..d0684752d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,7 +396,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", + "miniz_oxide 0.6.2", "object 0.30.3", "rustc-demangle", ] diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 56e7b4513..f65e3c7fc 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -58,7 +58,10 @@ macro_rules! decl_common_types { frame_system::ChainContext, Runtime, AllPalletsWithSystem, - zrml_prediction_markets::migrations::AddOutsiderBond, + ( + zrml_prediction_markets::migrations::AddOutsiderAndDisputeBond, + zrml_prediction_markets::migrations::MoveDataToSimpleDisputes, + ), >; #[cfg(not(feature = "parachain"))] @@ -68,7 +71,10 @@ macro_rules! decl_common_types { frame_system::ChainContext, Runtime, AllPalletsWithSystem, - zrml_prediction_markets::migrations::AddOutsiderBond, + ( + zrml_prediction_markets::migrations::AddOutsiderAndDisputeBond, + zrml_prediction_markets::migrations::MoveDataToSimpleDisputes, + ), >; pub type Header = generic::Header; diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index f57aecdbd..1db7adc0d 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -332,6 +332,10 @@ mod pallet { let market_status = market.status; let market_account = >::market_account(market_id); + if Self::is_dispute_bond_pending(&market_id, &market, false) { + Self::unreserve_dispute_bond(&market_id)?; + } + // Slash outstanding bonds; see // https://github.com/zeitgeistpm/runtime-audit-1/issues/34#issuecomment-1120187097 for // details. @@ -2055,9 +2059,6 @@ mod pallet { if Self::is_outsider_bond_pending(market_id, market, false) { Self::slash_outsider_bond(market_id, None)?; } - if Self::is_dispute_bond_pending(market_id, market, false) { - Self::slash_dispute_bond(market_id, None)?; - } Ok(()) } @@ -2523,8 +2524,6 @@ mod pallet { let resolved_outcome = resolved_outcome_option.unwrap_or_else(|| report.outcome.clone()); - let mut overall_imbalance = >::zero(); - // If the oracle reported right, return the OracleBond, otherwise slash it to // pay the correct reporters. let mut overall_imbalance = NegativeImbalanceOf::::zero(); @@ -2569,14 +2568,16 @@ mod pallet { } let mut correct_disputor = None; - if Self::is_dispute_bond_pending(market_id, market, false) { - if is_correct { - let imb = Self::slash_dispute_bond(market_id, None)?; - overall_imbalance.subsume(imb); - } else { - // If the report outcome was wrong, the dispute was justified - Self::unreserve_dispute_bond(market_id)?; - correct_disputor = Some(bond.who); + if let Some(bond) = market.bonds.dispute.clone() { + if !bond.is_settled { + if is_correct { + let imb = Self::slash_dispute_bond(market_id, None)?; + overall_imbalance.subsume(imb); + } else { + // If the report outcome was wrong, the dispute was justified + Self::unreserve_dispute_bond(market_id)?; + correct_disputor = Some(bond.who); + } } } diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index f3e2fe6ae..c9b1629f0 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -41,9 +41,7 @@ use zeitgeist_primitives::types::{ Asset, Bond, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, }; -#[cfg(feature = "try-runtime")] -use zrml_market_commons::MarketCommonsPalletApi; -use zrml_market_commons::Pallet as MarketCommonsPallet; +use zrml_market_commons::{MarketCommonsPalletApi, Pallet as MarketCommonsPallet}; #[cfg(any(feature = "try-runtime", test))] const MARKET_COMMONS: &[u8] = b"MarketCommons"; @@ -111,35 +109,53 @@ impl OnRuntimeUpgrade for AddOutsiderAn log::info!("AddOutsiderAndDisputeBond: Starting..."); let mut translated = 0u64; - zrml_market_commons::Markets::::translate::, _>(|_key, old_market| { - translated.saturating_inc(); - - let new_market = Market { - base_asset: old_market.base_asset, - creator: old_market.creator, - creation: old_market.creation, - creator_fee: old_market.creator_fee, - oracle: old_market.oracle, - metadata: old_market.metadata, - market_type: old_market.market_type, - period: old_market.period, - scoring_rule: old_market.scoring_rule, - status: old_market.status, - report: old_market.report, - resolved_outcome: old_market.resolved_outcome, - dispute_mechanism: old_market.dispute_mechanism, - deadlines: old_market.deadlines, - bonds: MarketBonds { - creation: old_market.bonds.creation, - oracle: old_market.bonds.oracle, - outsider: None, - // TODO - dispute: None, - }, - }; - - Some(new_market) - }); + zrml_market_commons::Markets::::translate::, _>( + |market_id, old_market| { + translated.saturating_inc(); + + let mut dispute_bond = None; + // SimpleDisputes is regarded in the following migration `MoveDataToSimpleDisputes` + if let MarketDisputeMechanism::Authorized = old_market.dispute_mechanism { + total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); + let old_disputes = crate::Disputes::::get(market_id); + if let Some(first_dispute) = old_disputes.first() { + let OldMarketDispute { at: _, by, outcome: _ } = first_dispute; + dispute_bond = Some(Bond::new(by.clone(), T::DisputeBond::get())); + } else { + log::warn!( + "MoveDataToSimpleDisputes: Could not find first dispute for market id \ + {:?}", + market_id + ); + } + } + + let new_market = Market { + base_asset: old_market.base_asset, + creator: old_market.creator, + creation: old_market.creation, + creator_fee: old_market.creator_fee, + oracle: old_market.oracle, + metadata: old_market.metadata, + market_type: old_market.market_type, + period: old_market.period, + scoring_rule: old_market.scoring_rule, + status: old_market.status, + report: old_market.report, + resolved_outcome: old_market.resolved_outcome, + dispute_mechanism: old_market.dispute_mechanism, + deadlines: old_market.deadlines, + bonds: MarketBonds { + creation: old_market.bonds.creation, + oracle: old_market.bonds.oracle, + outsider: None, + dispute: dispute_bond, + }, + }; + + Some(new_market) + }, + ); log::info!("AddOutsiderAndDisputeBond: Upgraded {} markets.", translated); total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(translated, translated)); @@ -202,12 +218,31 @@ impl OnRuntimeUpgrade for AddOutsiderAn assert_eq!(new_market.dispute_mechanism, old_market.dispute_mechanism); assert_eq!(new_market.bonds.oracle, old_market.bonds.oracle); assert_eq!(new_market.bonds.creation, old_market.bonds.creation); - // new field + // new fields assert_eq!(new_market.bonds.outsider, None); - // TODO - assert_eq!(new_market.bonds.dispute, None); + // other dispute mechanisms are regarded in the migration after this migration + if let MarketDisputeMechanism::Authorized = new_market.dispute_mechanism { + let old_disputes = crate::Disputes::::get(market_id); + if let Some(first_dispute) = old_disputes.first() { + let OldMarketDispute { at: _, by, outcome: _ } = first_dispute; + assert_eq!( + new_market.bonds.dispute, + Some(Bond { + who: by.clone(), + value: T::DisputeBond::get(), + is_settled: false + }) + ); + } + } else { + assert_eq!(new_market.bonds.dispute, None); + } } - log::info!("AddOutsiderAndDisputeBond: Market Counter post-upgrade is {}!", new_market_count); + + log::info!( + "AddOutsiderAndDisputeBond: Market Counter post-upgrade is {}!", + new_market_count + ); assert!(new_market_count > 0); Ok(()) } @@ -217,7 +252,7 @@ impl OnRuntimeUpgrade for AddOutsiderAn mod tests { use super::*; use crate::{ - mock::{ExtBuilder, Runtime}, + mock::{DisputeBond, ExtBuilder, Runtime}, MarketIdOf, MarketOf, }; use frame_support::{ @@ -229,7 +264,7 @@ mod tests { fn on_runtime_upgrade_increments_the_storage_version() { ExtBuilder::default().build().execute_with(|| { set_up_version(); - AddOutsiderBond::::on_runtime_upgrade(); + AddOutsiderAndDisputeBond::::on_runtime_upgrade(); assert_eq!( StorageVersion::get::>(), MARKET_COMMONS_NEXT_STORAGE_VERSION @@ -241,29 +276,51 @@ mod tests { fn on_runtime_upgrade_is_noop_if_versions_are_not_correct() { ExtBuilder::default().build().execute_with(|| { // Don't set up chain to signal that storage is already up to date. - let (_, new_markets) = construct_old_new_tuple(); + let (_, new_markets) = construct_old_new_tuple(None); populate_test_data::, MarketOf>( MARKET_COMMONS, MARKETS, new_markets.clone(), ); - AddOutsiderBond::::on_runtime_upgrade(); + AddOutsiderAndDisputeBond::::on_runtime_upgrade(); let actual = >::market(&0u128).unwrap(); assert_eq!(actual, new_markets[0]); }); } #[test] - fn on_runtime_upgrade_correctly_updates_markets() { + fn on_runtime_upgrade_correctly_updates_markets_with_none_disputor() { ExtBuilder::default().build().execute_with(|| { set_up_version(); - let (old_markets, new_markets) = construct_old_new_tuple(); + let (old_markets, new_markets) = construct_old_new_tuple(None); populate_test_data::, OldMarketOf>( MARKET_COMMONS, MARKETS, old_markets, ); - AddOutsiderBond::::on_runtime_upgrade(); + AddOutsiderAndDisputeBond::::on_runtime_upgrade(); + let actual = >::market(&0u128).unwrap(); + assert_eq!(actual, new_markets[0]); + }); + } + + #[test] + fn on_runtime_upgrade_correctly_updates_markets_with_some_disputor() { + ExtBuilder::default().build().execute_with(|| { + set_up_version(); + let mut disputes = crate::Disputes::::get(0); + let disputor = crate::mock::EVE; + let dispute = + OldMarketDispute { at: 0, by: disputor, outcome: OutcomeReport::Categorical(0u16) }; + disputes.try_push(dispute).unwrap(); + crate::Disputes::::insert(0, disputes); + let (old_markets, new_markets) = construct_old_new_tuple(Some(disputor)); + populate_test_data::, OldMarketOf>( + MARKET_COMMONS, + MARKETS, + old_markets, + ); + AddOutsiderAndDisputeBond::::on_runtime_upgrade(); let actual = >::market(&0u128).unwrap(); assert_eq!(actual, new_markets[0]); }); @@ -274,7 +331,9 @@ mod tests { .put::>(); } - fn construct_old_new_tuple() -> (Vec>, Vec>) { + fn construct_old_new_tuple( + disputor: Option, + ) -> (Vec>, Vec>) { let base_asset = Asset::Ztg; let creator = 999; let creator_fee = 1; @@ -293,12 +352,12 @@ mod tests { creation: Some(Bond::new(creator, ::ValidityBond::get())), oracle: Some(Bond::new(creator, ::OracleBond::get())), }; + let dispute_bond = disputor.map(|disputor| Bond::new(disputor, DisputeBond::get())); let new_bonds = MarketBonds { creation: Some(Bond::new(creator, ::ValidityBond::get())), oracle: Some(Bond::new(creator, ::OracleBond::get())), outsider: None, - // TODO - dispute: None, + dispute: dispute_bond, }; let old_market = OldMarket { @@ -353,11 +412,8 @@ mod tests { } } -#[cfg(feature = "try-runtime")] -use alloc::string::ToString; -use zrml_market_commons::MarketCommonsPalletApi; -use sp_runtime::SaturatedConversion; use frame_support::dispatch::EncodeLike; +use sp_runtime::SaturatedConversion; use zeitgeist_primitives::types::{MarketDispute, OldMarketDispute}; const PREDICTION_MARKETS_REQUIRED_STORAGE_VERSION: u16 = 7; @@ -398,39 +454,16 @@ where total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); + // important drain disputes storage item from prediction markets pallet for (market_id, old_disputes) in crate::Disputes::::drain() { total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); - if let Ok(mut market) = >::market(&market_id) { + if let Ok(market) = >::market(&market_id) { match market.dispute_mechanism { - MarketDisputeMechanism::Authorized => { - // TODO Move this part of the migration (market mutation) - // TODO into the OutsiderBond migration (needs to run before this migration) - // TODO because it needs to iterate over all Disputes of the pm pallet - // TODO in this current migration we drain all Disputes of the pm pallet - if let Some(first_dispute) = old_disputes.first() { - let OldMarketDispute { at: _, by, outcome: _ } = first_dispute; - market.bonds.dispute = - Some(Bond::new(by.clone(), T::DisputeBond::get())); - - total_weight = - total_weight.saturating_add(T::DbWeight::get().writes(1)); - zrml_market_commons::Markets::::insert(market_id, market); - } else { - log::warn!( - "MoveDataToSimpleDisputes: Could not find first dispute for \ - market id {:?}", - market_id - ); - } - // for authorized use the first dispute as actual dispute caller - continue; - } - // for simple-disputes ignore who called the dispute the first time - // and just use the below code to fill Disputes inside simple-disputes + MarketDisputeMechanism::Authorized => continue, + // just transform SimpleDispute disputes MarketDisputeMechanism::SimpleDisputes => (), - // ignore / delete all disputes for court markets MarketDisputeMechanism::Court => continue, } } else { @@ -544,16 +577,6 @@ where match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - let first_dispute = o - .first() - .expect(&format!("First dispute for market {:?} not found", market_id)[..]); - let disputor = first_dispute.by.clone(); - let bond = T::DisputeBond::get(); - assert_eq!( - market.bonds.dispute, - Some(Bond { who: disputor, value: bond, is_settled: false }), - ); - let simple_disputes_count = disputes.iter().count(); assert_eq!(simple_disputes_count, 0); continue; diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 924e725e2..9f2a5e7b0 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -4250,24 +4250,21 @@ fn outsider_reports_wrong_outcome() { let dispute_at_0 = report_at + 1; run_to_block(dispute_at_0); - let eve_reserved = Balances::reserved_balance(&EVE); - - assert_ok!(PredictionMarkets::dispute( - Origin::signed(EVE), - 0, - )); + assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); + check_reserve(&EVE, DisputeBond::get()); - assert_eq!(eve_reserved, DisputeBond::get()); - assert_ok!(SimpleDisputes::reserve_outcome( - Origin::signed(EVE), + Origin::signed(DAVE), 0, OutcomeReport::Categorical(0) )); - - assert_eq!(eve_reserved, DisputeBond::get() + zrml_simple_disputes::default_outcome_bond::(0)); + + let outcome_bond = zrml_simple_disputes::default_outcome_bond::(0); + + check_reserve(&DAVE, outcome_bond); let eve_balance_before = Balances::free_balance(&EVE); + let dave_balance_before = Balances::free_balance(&DAVE); // on_resolution called run_blocks(market.deadlines.dispute_duration); @@ -4277,12 +4274,13 @@ fn outsider_reports_wrong_outcome() { check_reserve(&outsider, 0); assert_eq!(Balances::free_balance(&outsider), outsider_balance_before); - let dispute_bond = crate::default_dispute_bond::(0usize); - // disputor EVE gets the OracleBond and OutsiderBond and dispute bond + // disputor EVE gets the OracleBond and OutsiderBond and DisputeBond assert_eq!( Balances::free_balance(&EVE), - eve_balance_before + dispute_bond + OutsiderBond::get() + OracleBond::get() + eve_balance_before + DisputeBond::get() + OutsiderBond::get() + OracleBond::get() ); + // DAVE gets his outcome bond back + assert_eq!(Balances::free_balance(&DAVE), dave_balance_before + outcome_bond); }; ExtBuilder::default().build().execute_with(|| { test(Asset::Ztg); From 78f0b0301dba795c5f2d6bc2e7b89bc2724168ad Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 31 Jan 2023 14:38:57 +0100 Subject: [PATCH 051/368] add migration tests --- zrml/prediction-markets/src/migrations.rs | 161 ++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index c9b1629f0..f75520be2 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -628,6 +628,167 @@ where } } +#[cfg(test)] +mod tests_simple_disputes_migration { + use super::*; + use crate::{ + mock::{DisputeBond, ExtBuilder, Runtime}, + MarketOf, + }; + use zrml_market_commons::MarketCommonsPalletApi; + + #[test] + fn on_runtime_upgrade_increments_the_storage_version() { + ExtBuilder::default().build().execute_with(|| { + set_up_version(); + MoveDataToSimpleDisputes::::on_runtime_upgrade(); + assert_eq!( + StorageVersion::get::>(), + PREDICTION_MARKETS_NEXT_STORAGE_VERSION + ); + }); + } + + #[test] + fn on_runtime_upgrade_is_noop_if_versions_are_not_correct() { + ExtBuilder::default().build().execute_with(|| { + // Don't set up chain to signal that storage is already up to date. + let market_id = 0u128; + let mut disputes = zrml_simple_disputes::Disputes::::get(market_id); + let dispute = MarketDispute { + at: 42u64, + by: 0u128, + outcome: OutcomeReport::Categorical(0u16), + bond: DisputeBond::get(), + }; + disputes.try_push(dispute.clone()).unwrap(); + zrml_simple_disputes::Disputes::::insert(market_id, disputes); + let market = get_market(MarketDisputeMechanism::SimpleDisputes); + >::push_market(market).unwrap(); + + MoveDataToSimpleDisputes::::on_runtime_upgrade(); + + let actual = zrml_simple_disputes::Disputes::::get(0); + assert_eq!(actual, vec![dispute]); + }); + } + + #[test] + fn on_runtime_upgrade_correctly_updates_simple_disputes() { + ExtBuilder::default().build().execute_with(|| { + set_up_version(); + let market_id = 0u128; + + let mut disputes = crate::Disputes::::get(0); + for i in 0..::MaxDisputes::get() { + let dispute = OldMarketDispute { + at: i as u64 + 42u64, + by: i as u128, + outcome: OutcomeReport::Categorical(i as u16), + }; + disputes.try_push(dispute).unwrap(); + } + crate::Disputes::::insert(market_id, disputes); + let market = get_market(MarketDisputeMechanism::SimpleDisputes); + >::push_market(market).unwrap(); + + MoveDataToSimpleDisputes::::on_runtime_upgrade(); + + let mut disputes = zrml_simple_disputes::Disputes::::get(market_id); + for i in 0..::MaxDisputes::get() { + let dispute = disputes.get_mut(i as usize).unwrap(); + + assert_eq!(dispute.at, i as u64 + 42u64); + assert_eq!(dispute.by, i as u128); + assert_eq!(dispute.outcome, OutcomeReport::Categorical(i as u16)); + + let bond = zrml_simple_disputes::default_outcome_bond::(i as usize); + assert_eq!(dispute.bond, bond); + } + }); + } + + #[test] + fn on_runtime_upgrade_correctly_updates_reserve_ids() { + ExtBuilder::default().build().execute_with(|| { + set_up_version(); + let market_id = 0u128; + + let mut disputes = crate::Disputes::::get(0); + for i in 0..::MaxDisputes::get() { + let dispute = OldMarketDispute { + at: i as u64 + 42u64, + by: i as u128, + outcome: OutcomeReport::Categorical(i as u16), + }; + disputes.try_push(dispute).unwrap(); + } + crate::Disputes::::insert(market_id, disputes); + let market = get_market(MarketDisputeMechanism::SimpleDisputes); + >::push_market(market).unwrap(); + + MoveDataToSimpleDisputes::::on_runtime_upgrade(); + + let mut disputes = zrml_simple_disputes::Disputes::::get(market_id); + for i in 0..::MaxDisputes::get() { + let dispute = disputes.get_mut(i as usize).unwrap(); + + assert_eq!(dispute.at, i as u64 + 42u64); + assert_eq!(dispute.by, i as u128); + assert_eq!(dispute.outcome, OutcomeReport::Categorical(i as u16)); + + let bond = zrml_simple_disputes::default_outcome_bond::(i as usize); + assert_eq!(dispute.bond, bond); + } + }); + } + + fn set_up_version() { + StorageVersion::new(PREDICTION_MARKETS_REQUIRED_STORAGE_VERSION) + .put::>(); + } + + fn get_market(dispute_mechanism: MarketDisputeMechanism) -> MarketOf { + let base_asset = Asset::Ztg; + let creator = 999; + let creator_fee = 1; + let oracle = 2; + let metadata = vec![3, 4, 5]; + let market_type = MarketType::Categorical(6); + let period = MarketPeriod::Block(7..8); + let scoring_rule = ScoringRule::CPMM; + let status = MarketStatus::Disputed; + let creation = MarketCreation::Permissionless; + let report = None; + let resolved_outcome = None; + let deadlines = Deadlines::default(); + let bonds = MarketBonds { + creation: Some(Bond::new(creator, ::ValidityBond::get())), + oracle: Some(Bond::new(creator, ::OracleBond::get())), + outsider: None, + dispute: None, + }; + + Market { + base_asset, + creator, + creation, + creator_fee, + oracle, + metadata, + market_type, + period, + scoring_rule, + status, + report, + resolved_outcome, + dispute_mechanism, + deadlines, + bonds, + } + } +} + // We use these utilities to prevent having to make the swaps pallet a dependency of // prediciton-markets. The calls are based on the implementation of `StorageVersion`, found here: // https://github.com/paritytech/substrate/blob/bc7a1e6c19aec92bfa247d8ca68ec63e07061032/frame/support/src/traits/metadata.rs#L168-L230 From 84bb0214ec2348342ed0f9c02f9699bb1e390d43 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 31 Jan 2023 16:53:52 +0100 Subject: [PATCH 052/368] add migration reserve id test --- zrml/prediction-markets/src/migrations.rs | 35 +++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index f75520be2..5c4507fbb 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -635,6 +635,7 @@ mod tests_simple_disputes_migration { mock::{DisputeBond, ExtBuilder, Runtime}, MarketOf, }; + use orml_traits::NamedMultiReservableCurrency; use zrml_market_commons::MarketCommonsPalletApi; #[test] @@ -721,6 +722,15 @@ mod tests_simple_disputes_migration { by: i as u128, outcome: OutcomeReport::Categorical(i as u16), }; + let bond = zrml_simple_disputes::default_outcome_bond::(i.into()); + let pm_reserve_id = crate::Pallet::::reserve_id(); + let res = ::AssetManager::reserve_named( + &pm_reserve_id, + Asset::Ztg, + &dispute.by, + bond.saturated_into::().saturated_into(), + ); + assert!(res.is_ok()); disputes.try_push(dispute).unwrap(); } crate::Disputes::::insert(market_id, disputes); @@ -733,12 +743,25 @@ mod tests_simple_disputes_migration { for i in 0..::MaxDisputes::get() { let dispute = disputes.get_mut(i as usize).unwrap(); - assert_eq!(dispute.at, i as u64 + 42u64); - assert_eq!(dispute.by, i as u128); - assert_eq!(dispute.outcome, OutcomeReport::Categorical(i as u16)); - - let bond = zrml_simple_disputes::default_outcome_bond::(i as usize); - assert_eq!(dispute.bond, bond); + let sd_reserve_id = zrml_simple_disputes::Pallet::::reserve_id(); + let reserved_balance = + ::AssetManager::reserved_balance_named( + &sd_reserve_id, + Asset::Ztg, + &dispute.by, + ); + let bond = zrml_simple_disputes::default_outcome_bond::(i.into()); + assert_eq!(reserved_balance, bond); + assert!(reserved_balance > 0); + + let pm_reserve_id = crate::Pallet::::reserve_id(); + let reserved_balance = + ::AssetManager::reserved_balance_named( + &pm_reserve_id, + Asset::Ztg, + &dispute.by, + ); + assert_eq!(reserved_balance, 0); } }); } From 8dea682dcc920c4b1e540fc553bf45d319ef29de Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 12:59:54 +0100 Subject: [PATCH 053/368] apply review suggestions --- zrml/global-disputes/README.md | 2 +- .../src/global_disputes_pallet_api.rs | 13 +------------ zrml/prediction-markets/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/zrml/global-disputes/README.md b/zrml/global-disputes/README.md index b71285897..27fef1ac2 100644 --- a/zrml/global-disputes/README.md +++ b/zrml/global-disputes/README.md @@ -45,7 +45,7 @@ on which the market finally resolves. total locked tokens. - `does_exist` - Check if the global dispute does already exist. - `is_active` - Check if the global dispute is active to get votes - (`vote_on_outcome`) and allow the addition of new vote outcomes with + (`vote_on_outcome`) and allow the addition of new voting outcomes with `add_vote_outcome`. - `start_global_dispute` - Start a global dispute. - `destroy_global_dispute` - Allow the users to get their voting funds and fee diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index fd78135fe..1de82fe1a 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -63,10 +63,7 @@ pub trait GlobalDisputesPalletApi { /// Returns the winning outcome. fn determine_voting_winner(market_id: &MarketId) -> Option; - /// Check if global dispute already exists. - /// - /// # Arguments - /// - `market_id` - The id of the market. + /// Check if a global dispute exists for the specified market. fn does_exist(market_id: &MarketId) -> bool; /// Check if global dispute is active or initialized. But not finished. @@ -76,14 +73,6 @@ pub trait GlobalDisputesPalletApi { /// - `market_id` - The id of the market. fn is_unfinished(market_id: &MarketId) -> bool; - /// Check if a global dispute does not exist. - /// - /// # Arguments - /// - `market_id` - The id of the market. - fn does_not_exist(market_id: &MarketId) -> bool { - !Self::does_exist(market_id) - } - /// Start a global dispute. /// /// # Arguments diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index d4db023ae..84cf97ad5 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1467,7 +1467,7 @@ mod pallet { ); ensure!( - T::GlobalDisputes::does_not_exist(&market_id), + !T::GlobalDisputes::does_exist(&market_id), Error::::GlobalDisputeExistsAlready ); From 4ef17feded70c1093a6f1bf626eb924e2d3761cb Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 13:22:02 +0100 Subject: [PATCH 054/368] move unused types --- Cargo.lock | 2 +- zrml/global-disputes/src/benchmarks.rs | 4 +-- zrml/global-disputes/src/lib.rs | 49 ++------------------------ zrml/global-disputes/src/tests.rs | 2 +- zrml/global-disputes/src/utils.rs | 47 ++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 51 deletions(-) create mode 100644 zrml/global-disputes/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 0c068e0e8..8383998b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,7 +396,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", + "miniz_oxide 0.6.2", "object 0.30.3", "rustc-demangle", ] diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index ad94e4793..f3a40f313 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -25,8 +25,8 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{ - global_disputes_pallet_api::GlobalDisputesPalletApi, market_mock, types::*, BalanceOf, Call, - Config, Pallet as GlobalDisputes, *, + global_disputes_pallet_api::GlobalDisputesPalletApi, types::*, utils::market_mock, BalanceOf, + Call, Config, Pallet as GlobalDisputes, *, }; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_support::{ diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index a5c91bd8a..e2e208466 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -24,6 +24,7 @@ pub mod migrations; mod mock; mod tests; pub mod types; +mod utils; pub mod weights; pub use global_disputes_pallet_api::GlobalDisputesPalletApi; @@ -51,25 +52,11 @@ mod pallet { DispatchError, DispatchResult, }; use sp_std::{vec, vec::Vec}; - use zeitgeist_primitives::{ - traits::DisputeResolutionApi, - types::{Asset, Market, OutcomeReport}, - }; + use zeitgeist_primitives::{traits::DisputeResolutionApi, types::OutcomeReport}; use zrml_market_commons::MarketCommonsPalletApi; - pub(crate) type MarketOf = Market< - ::AccountId, - MarketCommonsBalanceOf, - ::BlockNumber, - MomentOf, - Asset>, - >; pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; - pub(crate) type MarketCommonsBalanceOf = - as Currency<::AccountId>>::Balance; - pub(crate) type CurrencyOf = - <::MarketCommons as MarketCommonsPalletApi>::Currency; pub(crate) type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; @@ -886,35 +873,3 @@ mod pallet { } } } - -#[cfg(any(feature = "runtime-benchmarks", test))] -pub(crate) fn market_mock() -> MarketOf -where - T: crate::Config, -{ - use frame_support::traits::Get; - use sp_runtime::traits::AccountIdConversion; - use zeitgeist_primitives::types::ScoringRule; - - zeitgeist_primitives::types::Market { - base_asset: zeitgeist_primitives::types::Asset::Ztg, - creation: zeitgeist_primitives::types::MarketCreation::Permissionless, - creator_fee: 0, - creator: T::GlobalDisputesPalletId::get().into_account_truncating(), - market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=u128::MAX), - dispute_mechanism: zeitgeist_primitives::types::MarketDisputeMechanism::SimpleDisputes, - metadata: Default::default(), - oracle: T::GlobalDisputesPalletId::get().into_account_truncating(), - period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), - deadlines: zeitgeist_primitives::types::Deadlines { - grace_period: 1_u32.into(), - oracle_duration: 1_u32.into(), - dispute_duration: 1_u32.into(), - }, - report: None, - resolved_outcome: None, - scoring_rule: ScoringRule::CPMM, - status: zeitgeist_primitives::types::MarketStatus::Disputed, - bonds: Default::default(), - } -} diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 053424db4..e62be26c3 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -19,9 +19,9 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, - market_mock, mock::*, types::{GDInfo, GDStatus, OutcomeInfo, Possession}, + utils::market_mock, BalanceOf, Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, }; use frame_support::{ diff --git a/zrml/global-disputes/src/utils.rs b/zrml/global-disputes/src/utils.rs new file mode 100644 index 000000000..ac7c4fa6b --- /dev/null +++ b/zrml/global-disputes/src/utils.rs @@ -0,0 +1,47 @@ +#![cfg(any(feature = "runtime-benchmarks", test))] + +use crate::*; +use frame_support::traits::Currency; + +type CurrencyOf = + <::MarketCommons as zrml_market_commons::MarketCommonsPalletApi>::Currency; +type MarketCommonsBalanceOf = + as Currency<::AccountId>>::Balance; +type MarketOf = zeitgeist_primitives::types::Market< + ::AccountId, + MarketCommonsBalanceOf, + ::BlockNumber, + MomentOf, + zeitgeist_primitives::types::Asset>, +>; + +pub(crate) fn market_mock() -> MarketOf +where + T: crate::Config, +{ + use frame_support::traits::Get; + use sp_runtime::traits::AccountIdConversion; + use zeitgeist_primitives::types::ScoringRule; + + zeitgeist_primitives::types::Market { + base_asset: zeitgeist_primitives::types::Asset::Ztg, + creation: zeitgeist_primitives::types::MarketCreation::Permissionless, + creator_fee: 0, + creator: T::GlobalDisputesPalletId::get().into_account_truncating(), + market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=u128::MAX), + dispute_mechanism: zeitgeist_primitives::types::MarketDisputeMechanism::SimpleDisputes, + metadata: Default::default(), + oracle: T::GlobalDisputesPalletId::get().into_account_truncating(), + period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), + deadlines: zeitgeist_primitives::types::Deadlines { + grace_period: 1_u32.into(), + oracle_duration: 1_u32.into(), + dispute_duration: 1_u32.into(), + }, + report: None, + resolved_outcome: None, + scoring_rule: ScoringRule::CPMM, + status: zeitgeist_primitives::types::MarketStatus::Disputed, + bonds: Default::default(), + } +} From 76422fe4c69291ffde34cefdada573caa4c12c8e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 13:31:47 +0100 Subject: [PATCH 055/368] renaming --- zrml/global-disputes/src/utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/utils.rs b/zrml/global-disputes/src/utils.rs index ac7c4fa6b..9322ef1b8 100644 --- a/zrml/global-disputes/src/utils.rs +++ b/zrml/global-disputes/src/utils.rs @@ -5,11 +5,11 @@ use frame_support::traits::Currency; type CurrencyOf = <::MarketCommons as zrml_market_commons::MarketCommonsPalletApi>::Currency; -type MarketCommonsBalanceOf = +type BalanceOf = as Currency<::AccountId>>::Balance; type MarketOf = zeitgeist_primitives::types::Market< ::AccountId, - MarketCommonsBalanceOf, + BalanceOf, ::BlockNumber, MomentOf, zeitgeist_primitives::types::Asset>, From c7f82d84117cd9fe19df9a4cbbd84f27acd46ffc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 13:37:00 +0100 Subject: [PATCH 056/368] add todo issues --- zrml/global-disputes/src/lib.rs | 4 ++-- zrml/global-disputes/src/types.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index e2e208466..aa782178d 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -75,7 +75,7 @@ mod pallet { BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; type RewardInfoOf = RewardInfo, AccountIdOf, BalanceOf>; - // TODO: to remove after the storage migration + // TODO(#968): to remove after the storage migration pub type WinnerInfoOf = OldWinnerInfo, OwnerInfoOf>; /// The current storage version. @@ -174,7 +174,7 @@ mod pallet { pub type GlobalDisputesInfo = StorageMap<_, Twox64Concat, MarketIdOf, GDInfoOf, OptionQuery>; - // TODO: to remove after the storage migration + // TODO(#986): to remove after the storage migration #[pallet::storage] pub type Winners = StorageMap<_, Twox64Concat, MarketIdOf, WinnerInfoOf, OptionQuery>; diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 3c505aef0..5f371db9a 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -79,7 +79,7 @@ pub struct RewardInfo { pub source: AccountId, } -// TODO: to remove after the storage migration +// TODO(#986): to remove after the storage migration /// The information about a voting outcome of a global dispute. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] From 2924bc1a98223ea0cf1f38593b28c5bf33964925 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 13:48:03 +0100 Subject: [PATCH 057/368] apply review suggestion --- zrml/global-disputes/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index aa782178d..8b3773de9 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -115,8 +115,8 @@ mod pallet { /// The maximum numbers of distinct markets /// on which one account can simultaneously vote on outcomes. - /// Otherwise users can just keep voting on different global disputes and never unlock. /// When the user unlocks, the user has again `MaxGlobalDisputeVotes` number of votes. + /// This constant is useful to limit the number of for-loop iterations (weight constraints). #[pallet::constant] type MaxGlobalDisputeVotes: Get; From 79a25ec72e9687137687f72da9f874f801f84cc1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 13:55:02 +0100 Subject: [PATCH 058/368] update error --- zrml/global-disputes/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 8b3773de9..b19352c3f 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -248,6 +248,8 @@ mod pallet { AddOutcomePeriodIsOver, /// It is not inside the period in which votes are allowed. NotInVotePeriod, + /// The operation requires a global dispute in a destroyed state. + GlobalDisputeNotDestroyed, } #[pallet::call] @@ -338,7 +340,7 @@ mod pallet { let gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; - ensure!(gd_info.status == GDStatus::Destroyed, Error::::UnfinishedGlobalDispute); + ensure!(gd_info.status == GDStatus::Destroyed, Error::::GlobalDisputeNotDestroyed); let mut owners_len = 0u32; let mut removed_keys_amount = 0u32; From 4535b99f1981c25bdc2d6e8b372f127377ea6aba Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 14:04:52 +0100 Subject: [PATCH 059/368] fix owners_len --- zrml/global-disputes/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index b19352c3f..23e6133b9 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -357,7 +357,7 @@ mod pallet { )?; } Some(Possession::Shared { owners }) => { - owners_len = owners_len.max(owners.len() as u32); + owners_len = owners_len.saturating_add(owners.len() as u32); } None => (), } From 52925b62c3bb76ac0290609a6c71964bb799ebbb Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 14:12:59 +0100 Subject: [PATCH 060/368] update comments, owners len calc --- zrml/global-disputes/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 23e6133b9..66ab23a2a 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -409,7 +409,9 @@ mod pallet { owners_len = owners.len() as u32; } // storage write is needed here in case, - // that the first call of reward_outcome_owner doesn't reward the owners + // that the first call to purge_outcomes + // doesn't save the owners of the winning outcome + // saving this information is required to reward the winners // this can happen if there are more than RemoveKeysLimit keys to remove gd_info.outcome_info = outcome_info; >::insert(market_id, gd_info); @@ -420,7 +422,7 @@ mod pallet { >::drain_prefix(market_id).take(T::RemoveKeysLimit::get() as usize) { if let Some(Possession::Shared { owners }) = outcome_info.possession { - owners_len = owners_len.max(owners.len() as u32); + owners_len = owners_len.saturating_add(owners.len() as u32); } removed_keys_amount = removed_keys_amount.saturating_add(1u32); } From b075e0118572bdadef6a0a4500456b29ca71d541 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 14:19:25 +0100 Subject: [PATCH 061/368] fix log --- zrml/global-disputes/src/lib.rs | 4 ++-- zrml/global-disputes/src/utils.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 66ab23a2a..21c319fe3 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -409,7 +409,7 @@ mod pallet { owners_len = owners.len() as u32; } // storage write is needed here in case, - // that the first call to purge_outcomes + // that the first call to purge_outcomes // doesn't save the owners of the winning outcome // saving this information is required to reward the winners // this can happen if there are more than RemoveKeysLimit keys to remove @@ -717,7 +717,7 @@ mod pallet { } else { log::error!( "Global Disputes: There should be always at least one owner for a voting \ - outcome." + outcome. This can also happen if reward is smaller than owners_len." ); debug_assert!(false); } diff --git a/zrml/global-disputes/src/utils.rs b/zrml/global-disputes/src/utils.rs index 9322ef1b8..99def76c8 100644 --- a/zrml/global-disputes/src/utils.rs +++ b/zrml/global-disputes/src/utils.rs @@ -5,8 +5,7 @@ use frame_support::traits::Currency; type CurrencyOf = <::MarketCommons as zrml_market_commons::MarketCommonsPalletApi>::Currency; -type BalanceOf = - as Currency<::AccountId>>::Balance; +type BalanceOf = as Currency<::AccountId>>::Balance; type MarketOf = zeitgeist_primitives::types::Market< ::AccountId, BalanceOf, From 9ae9b277c56d1701cfeb76c9d7c18a16e9fc3ff4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 14:27:58 +0100 Subject: [PATCH 062/368] stay consistent for rewarding the outcome owners --- zrml/global-disputes/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 21c319fe3..b2ba0d608 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -732,12 +732,17 @@ mod pallet { reward_info: RewardInfoOf, owner: AccountIdOf, ) -> DispatchResultWithPostInfo { - T::Currency::transfer( + let res = T::Currency::transfer( &reward_info.source, &owner, reward_info.reward, ExistenceRequirement::AllowDeath, - )?; + ); + // not really much we can do if it fails + debug_assert!( + res.is_ok(), + "Global Disputes: Rewarding a outcome owner failed." + ); Self::deposit_event(Event::OutcomeOwnerRewarded { market_id: reward_info.market_id, owner, From 14ea6b4a1a45b7cba98091db389085458c80085a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 9 Feb 2023 14:29:37 +0100 Subject: [PATCH 063/368] correct error --- zrml/global-disputes/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index b2ba0d608..74cc71630 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -229,7 +229,7 @@ mod pallet { /// No global dispute present at the moment. NoGlobalDisputeInitialized, /// The global dispute has to be in initialized state during the initial outcome setup. - NotInSetupMode, + NotInitialized, /// There is no owner information for this outcome. NoPossession, /// The voting outcome has been already added. @@ -766,7 +766,7 @@ mod pallet { ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); if let Some(gd_info) = >::get(market_id) { - ensure!(gd_info.status == GDStatus::Initialized, Error::::NotInSetupMode); + ensure!(gd_info.status == GDStatus::Initialized, Error::::NotInitialized); } match >::get(market_id, &outcome) { From 7e7748a81f12de9b14132f01158d8f1650173767 Mon Sep 17 00:00:00 2001 From: Chralt Date: Thu, 9 Feb 2023 14:32:37 +0100 Subject: [PATCH 064/368] Apply suggestions from code review Co-authored-by: Malte Kliemann --- zrml/global-disputes/src/lib.rs | 6 +++--- zrml/global-disputes/src/migrations.rs | 5 ----- zrml/global-disputes/src/types.rs | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 74cc71630..5e15dd25e 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -212,7 +212,7 @@ mod pallet { #[pallet::error] pub enum Error { - /// Sender tried to vote with an amount below a defined minium. + /// Sender tried to vote with an amount below a defined minimum. AmountTooLow, /// To start a global dispute, at least two outcomes are required. AtLeastTwoOutcomesRequired, @@ -330,7 +330,7 @@ mod pallet { #[frame_support::transactional] #[pallet::weight(T::WeightInfo::refund_vote_fees( T::RemoveKeysLimit::get(), - T::MaxOwners::get() + T::MaxOwners::get(), ))] pub fn refund_vote_fees( origin: OriginFor, @@ -701,7 +701,7 @@ mod pallet { // *Should* always be equal to `reward_per_each` let reward = remainder.min(reward_per_each); remainder = remainder.saturating_sub(reward); - // Reward the loosing funds to the winners + // Reward the losing funds to the winners let res = T::Currency::transfer( &reward_info.source, winner, diff --git a/zrml/global-disputes/src/migrations.rs b/zrml/global-disputes/src/migrations.rs index 33b32f35c..36c268804 100644 --- a/zrml/global-disputes/src/migrations.rs +++ b/zrml/global-disputes/src/migrations.rs @@ -15,11 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -// We use these utilities to prevent having to make the swaps pallet a dependency of -// prediciton-markets. The calls are based on the implementation of `StorageVersion`, found here: -// https://github.com/paritytech/substrate/blob/bc7a1e6c19aec92bfa247d8ca68ec63e07061032/frame/support/src/traits/metadata.rs#L168-L230 -// and previous migrations. - extern crate alloc; use crate::{types::*, Config, Pallet as GDPallet, *}; diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 5f371db9a..5ba63b387 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -37,7 +37,7 @@ pub struct OutcomeInfo { /// The general information about the global dispute. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub struct GDInfo { - /// The outcome, which is in the lead. + /// The outcome which is in the lead. pub winner_outcome: OutcomeReport, /// The information about the winning outcome. pub outcome_info: OutcomeInfo, From 4a3624daeb6090bc2bce1f8f4ab52c3864211a24 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 07:57:28 +0100 Subject: [PATCH 065/368] add copyrights --- zrml/global-disputes/src/benchmarks.rs | 2 +- .../src/global_disputes_pallet_api.rs | 1 + zrml/global-disputes/src/lib.rs | 1 + zrml/global-disputes/src/migrations.rs | 1 + zrml/global-disputes/src/mock.rs | 1 + zrml/global-disputes/src/tests.rs | 1 + zrml/global-disputes/src/types.rs | 18 ++++++++++++++++++ zrml/global-disputes/src/utils.rs | 17 +++++++++++++++++ zrml/global-disputes/src/weights.rs | 1 + 9 files changed, 42 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index f3a40f313..ca2003611 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Forecasting Technologies LTD. +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 1de82fe1a..0045a9fc6 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 5e15dd25e..27fbfb580 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/global-disputes/src/migrations.rs b/zrml/global-disputes/src/migrations.rs index 36c268804..873511fc6 100644 --- a/zrml/global-disputes/src/migrations.rs +++ b/zrml/global-disputes/src/migrations.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/global-disputes/src/mock.rs b/zrml/global-disputes/src/mock.rs index 0048ab4f1..f389d6bf6 100644 --- a/zrml/global-disputes/src/mock.rs +++ b/zrml/global-disputes/src/mock.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index e62be26c3..527a9f56a 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 5ba63b387..6531ed319 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -1,3 +1,21 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + use frame_support::pallet_prelude::{Decode, Encode, MaxEncodedLen, TypeInfo}; use sp_runtime::traits::Saturating; use zeitgeist_primitives::types::OutcomeReport; diff --git a/zrml/global-disputes/src/utils.rs b/zrml/global-disputes/src/utils.rs index 99def76c8..fd0fc7bbb 100644 --- a/zrml/global-disputes/src/utils.rs +++ b/zrml/global-disputes/src/utils.rs @@ -1,3 +1,20 @@ +// Copyright 2023 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + #![cfg(any(feature = "runtime-benchmarks", test))] use crate::*; diff --git a/zrml/global-disputes/src/weights.rs b/zrml/global-disputes/src/weights.rs index c9b0133c4..5ca41e590 100644 --- a/zrml/global-disputes/src/weights.rs +++ b/zrml/global-disputes/src/weights.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. From b72401d3248d366d9898c5f5e7a77c9ab406a5c4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 08:09:08 +0100 Subject: [PATCH 066/368] rename struct and enum --- zrml/global-disputes/src/benchmarks.rs | 22 +++++++++--------- zrml/global-disputes/src/lib.rs | 32 +++++++++++++------------- zrml/global-disputes/src/migrations.rs | 16 ++++++------- zrml/global-disputes/src/tests.rs | 20 ++++++++-------- zrml/global-disputes/src/types.rs | 10 ++++---- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index ca2003611..973addd58 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -103,9 +103,9 @@ benchmarks! { let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); let vote_end = add_outcome_end + T::VotePeriod::get(); - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: outcome.clone(), - status: GDStatus::Active { add_outcome_end, vote_end }, + status: GdStatus::Active { add_outcome_end, vote_end }, outcome_info, }; >::insert(market_id, gd_info); @@ -142,9 +142,9 @@ benchmarks! { let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); let vote_end = add_outcome_end + T::VotePeriod::get(); - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: outcome, - status: GDStatus::Active { add_outcome_end, vote_end }, + status: GdStatus::Active { add_outcome_end, vote_end }, outcome_info }; @@ -187,7 +187,7 @@ benchmarks! { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; // is_finished is true, // because we need `lock_needed` to be zero to remove all locks. - let gd_info = GDInfo {winner_outcome: outcome, status: GDStatus::Finished, outcome_info}; + let gd_info = GlobalDisputeInfo {winner_outcome: outcome, status: GdStatus::Finished, outcome_info}; let caller: T::AccountId = whitelisted_caller(); let voter: T::AccountId = account("voter", 0, 0); @@ -235,9 +235,9 @@ benchmarks! { let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); let vote_end = add_outcome_end + T::VotePeriod::get(); - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(0), - status: GDStatus::Active { add_outcome_end, vote_end }, + status: GdStatus::Active { add_outcome_end, vote_end }, outcome_info, }; @@ -280,9 +280,9 @@ benchmarks! { } let owners = BoundedVec::try_from(owners_vec.clone()).unwrap(); let possession = Some(Possession::Shared { owners }); - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(0), - status: GDStatus::Finished, + status: GdStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession, @@ -367,7 +367,7 @@ benchmarks! { outcome_sum: 42u128.saturated_into(), possession, }; - let gd_info = GDInfo {winner_outcome, status: GDStatus::Finished, outcome_info}; + let gd_info = GlobalDisputeInfo {winner_outcome, status: GdStatus::Finished, outcome_info}; >::insert(market_id, gd_info); let caller: T::AccountId = whitelisted_caller(); @@ -422,7 +422,7 @@ benchmarks! { outcome_sum: 42u128.saturated_into(), possession, }; - let gd_info = GDInfo {winner_outcome, status: GDStatus::Destroyed, outcome_info}; + let gd_info = GlobalDisputeInfo {winner_outcome, status: GdStatus::Destroyed, outcome_info}; >::insert(market_id, gd_info); let caller: T::AccountId = whitelisted_caller(); diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 27fbfb580..df259809d 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -64,7 +64,7 @@ mod pallet { pub type AccountIdOf = ::AccountId; pub type OutcomeInfoOf = OutcomeInfo, BalanceOf, OwnerInfoOf>; - pub type GDInfoOf = GDInfo< + pub type GlobalDisputeInfoOf = GlobalDisputeInfo< AccountIdOf, BalanceOf, OwnerInfoOf, @@ -173,7 +173,7 @@ mod pallet { /// about the global dispute. #[pallet::storage] pub type GlobalDisputesInfo = - StorageMap<_, Twox64Concat, MarketIdOf, GDInfoOf, OptionQuery>; + StorageMap<_, Twox64Concat, MarketIdOf, GlobalDisputeInfoOf, OptionQuery>; // TODO(#986): to remove after the storage migration #[pallet::storage] @@ -283,7 +283,7 @@ mod pallet { let gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; let now = >::block_number(); - if let GDStatus::Active { add_outcome_end, vote_end: _ } = gd_info.status { + if let GdStatus::Active { add_outcome_end, vote_end: _ } = gd_info.status { ensure!(now <= add_outcome_end, Error::::AddOutcomePeriodIsOver); } else { return Err(Error::::InvalidGlobalDisputeStatus.into()); @@ -341,7 +341,7 @@ mod pallet { let gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; - ensure!(gd_info.status == GDStatus::Destroyed, Error::::GlobalDisputeNotDestroyed); + ensure!(gd_info.status == GdStatus::Destroyed, Error::::GlobalDisputeNotDestroyed); let mut owners_len = 0u32; let mut removed_keys_amount = 0u32; @@ -399,7 +399,7 @@ mod pallet { let mut gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; - ensure!(gd_info.status == GDStatus::Finished, Error::::UnfinishedGlobalDispute); + ensure!(gd_info.status == GdStatus::Finished, Error::::UnfinishedGlobalDispute); let winning_outcome: Option> = >::get(market_id, &gd_info.winner_outcome); @@ -463,7 +463,7 @@ mod pallet { let gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; - ensure!(gd_info.status == GDStatus::Finished, Error::::UnfinishedGlobalDispute); + ensure!(gd_info.status == GdStatus::Finished, Error::::UnfinishedGlobalDispute); let reward_account = Self::reward_account(&market_id); let reward_account_free_balance = T::Currency::free_balance(&reward_account); @@ -518,7 +518,7 @@ mod pallet { let gd_info = >::get(market_id) .ok_or(Error::::NoGlobalDisputeInitialized)?; let now = >::block_number(); - if let GDStatus::Active { add_outcome_end, vote_end } = gd_info.status { + if let GdStatus::Active { add_outcome_end, vote_end } = gd_info.status { ensure!(add_outcome_end < now && now <= vote_end, Error::::NotInVotePeriod); } else { return Err(Error::::InvalidGlobalDisputeStatus.into()); @@ -622,7 +622,7 @@ mod pallet { // weight component MaxOwners comes from querying the winner information match >::get(market_id) { Some(gd_info) => { - if matches!(gd_info.status, GDStatus::Finished | GDStatus::Destroyed) { + if matches!(gd_info.status, GdStatus::Finished | GdStatus::Destroyed) { false } else { lock_needed = lock_needed.max(locked_balance); @@ -675,9 +675,9 @@ mod pallet { } fn update_winner(market_id: &MarketIdOf, outcome: &OutcomeReport, amount: BalanceOf) { - >::mutate(market_id, |highest: &mut Option>| { + >::mutate(market_id, |highest: &mut Option>| { *highest = Some(highest.clone().map_or( - GDInfo::new(outcome.clone(), amount), + GlobalDisputeInfo::new(outcome.clone(), amount), |mut prev_gd_info| { if amount >= prev_gd_info.outcome_info.outcome_sum { prev_gd_info.update_winner(outcome.clone(), amount); @@ -767,7 +767,7 @@ mod pallet { ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); if let Some(gd_info) = >::get(market_id) { - ensure!(gd_info.status == GDStatus::Initialized, Error::::NotInitialized); + ensure!(gd_info.status == GdStatus::Initialized, Error::::NotInitialized); } match >::get(market_id, &outcome) { @@ -818,7 +818,7 @@ mod pallet { fn determine_voting_winner(market_id: &MarketIdOf) -> Option { match >::get(market_id) { Some(mut gd_info) => { - gd_info.status = GDStatus::Finished; + gd_info.status = GdStatus::Finished; let winner_outcome = gd_info.winner_outcome.clone(); >::insert(market_id, gd_info); Self::deposit_event(Event::GlobalDisputeWinnerDetermined { @@ -838,7 +838,7 @@ mod pallet { if let Some(gd_info) = >::get(market_id) { return matches!( gd_info.status, - GDStatus::Active { add_outcome_end: _, vote_end: _ } | GDStatus::Initialized + GdStatus::Active { add_outcome_end: _, vote_end: _ } | GdStatus::Initialized ); } false @@ -861,7 +861,7 @@ mod pallet { >::try_mutate(market_id, |gd_info| -> DispatchResult { let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; - raw_gd_info.status = GDStatus::Active { add_outcome_end, vote_end }; + raw_gd_info.status = GdStatus::Active { add_outcome_end, vote_end }; *gd_info = Some(raw_gd_info.clone()); Ok(()) })?; @@ -873,8 +873,8 @@ mod pallet { >::try_mutate(market_id, |gd_info| { let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; - raw_gd_info.status = GDStatus::Destroyed; - if let GDStatus::Active { add_outcome_end: _, vote_end } = raw_gd_info.status { + raw_gd_info.status = GdStatus::Destroyed; + if let GdStatus::Active { add_outcome_end: _, vote_end } = raw_gd_info.status { T::DisputeResolution::remove_auto_resolve(market_id, vote_end); } *gd_info = Some(raw_gd_info.clone()); diff --git a/zrml/global-disputes/src/migrations.rs b/zrml/global-disputes/src/migrations.rs index 873511fc6..80d0b5e43 100644 --- a/zrml/global-disputes/src/migrations.rs +++ b/zrml/global-disputes/src/migrations.rs @@ -75,10 +75,10 @@ impl OnRuntimeUpgrade let outcome_info = OutcomeInfo { outcome_sum: winner_info.outcome_info.outcome_sum, possession }; - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: winner_info.outcome, outcome_info, - status: GDStatus::Finished, + status: GdStatus::Finished, }; crate::GlobalDisputesInfo::::insert(market_id, gd_info); total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); @@ -141,7 +141,7 @@ impl OnRuntimeUpgrade let mut markets_count = 0_u32; let old_counter_key = "counter_key".to_string(); for (market_id, gd_info) in crate::GlobalDisputesInfo::::iter() { - let GDInfo { winner_outcome, outcome_info, status } = gd_info; + let GlobalDisputeInfo { winner_outcome, outcome_info, status } = gd_info; let market_id_str = format!("{:?}", market_id); @@ -150,7 +150,7 @@ impl OnRuntimeUpgrade .unwrap_or_else(|| panic!("old value not found for market id {:?}", market_id)); assert_eq!(winner_outcome, winner_info.outcome); - assert_eq!(status, GDStatus::Finished); + assert_eq!(status, GdStatus::Finished); let owners = winner_info.outcome_info.owners; let owners_len = owners.len(); @@ -235,10 +235,10 @@ mod tests { let new_outcome_info = OutcomeInfo { outcome_sum, possession }; - let expected = GDInfo { + let expected = GlobalDisputeInfo { winner_outcome: outcome, outcome_info: new_outcome_info, - status: GDStatus::Finished, + status: GdStatus::Finished, }; let actual = crate::GlobalDisputesInfo::::get(market_id).unwrap(); @@ -271,10 +271,10 @@ mod tests { let new_outcome_info = OutcomeInfo { outcome_sum, possession }; - let expected = GDInfo { + let expected = GlobalDisputeInfo { winner_outcome: outcome, outcome_info: new_outcome_info, - status: GDStatus::Finished, + status: GdStatus::Finished, }; let actual = crate::GlobalDisputesInfo::::get(market_id).unwrap(); diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 527a9f56a..eaaf58711 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -21,7 +21,7 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, mock::*, - types::{GDInfo, GDStatus, OutcomeInfo, Possession}, + types::{GlobalDisputeInfo, GdStatus, OutcomeInfo, Possession}, utils::market_mock, BalanceOf, Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, }; @@ -179,8 +179,8 @@ fn add_vote_outcome_fails_if_global_dispute_finished() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - let mut gd_info = GDInfo::new(OutcomeReport::Scalar(0), 10 * BASE); - gd_info.status = GDStatus::Finished; + let mut gd_info = GlobalDisputeInfo::new(OutcomeReport::Scalar(0), 10 * BASE); + gd_info.status = GdStatus::Finished; >::insert(market_id, gd_info); assert_noop!( @@ -262,9 +262,9 @@ fn reward_outcome_owner_works_for_multiple_owners() { &GlobalDisputes::reward_account(&market_id), 3 * VotingOutcomeFee::get(), ); - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(20), - status: GDStatus::Finished, + status: GdStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, possession: Some(Possession::Shared { owners: Default::default() }), @@ -319,9 +319,9 @@ fn reward_outcome_owner_has_dust() { }, ); let _ = Balances::deposit_creating(&GlobalDisputes::reward_account(&market_id), 100 * BASE); - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(20), - status: GDStatus::Finished, + status: GdStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, possession: Some(Possession::Shared { owners: Default::default() }), @@ -357,9 +357,9 @@ fn reward_outcome_owner_works_for_one_owner() { &GlobalDisputes::reward_account(&market_id), 3 * VotingOutcomeFee::get(), ); - let gd_info = GDInfo { + let gd_info = GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(20), - status: GDStatus::Finished, + status: GdStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, possession: Some(Possession::Shared { @@ -707,7 +707,7 @@ fn determine_voting_winner_works_four_outcome_votes() { assert_eq!( >::get(market_id).unwrap().status, - GDStatus::Finished + GdStatus::Finished ); }); } diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 6531ed319..d657d48fe 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -54,21 +54,21 @@ pub struct OutcomeInfo { /// The general information about the global dispute. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct GDInfo { +pub struct GlobalDisputeInfo { /// The outcome which is in the lead. pub winner_outcome: OutcomeReport, /// The information about the winning outcome. pub outcome_info: OutcomeInfo, /// The current status of the global dispute. - pub status: GDStatus, + pub status: GdStatus, } impl - GDInfo + GlobalDisputeInfo { pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession: None }; - GDInfo { winner_outcome: outcome, status: GDStatus::Initialized, outcome_info } + GlobalDisputeInfo { winner_outcome: outcome, status: GdStatus::Initialized, outcome_info } } pub fn update_winner(&mut self, outcome: OutcomeReport, vote_sum: Balance) { @@ -78,7 +78,7 @@ impl } #[derive(TypeInfo, Debug, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub enum GDStatus { +pub enum GdStatus { /// The global dispute is initialized. Initialized, /// The global dispute is in progress. Save the addition of outcome end and vote end. From 71aa7550fef6bc490a902519702ba64d9a836815 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 08:19:49 +0100 Subject: [PATCH 067/368] document data types --- zrml/global-disputes/src/types.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index d657d48fe..8ee5eb95f 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -20,9 +20,14 @@ use frame_support::pallet_prelude::{Decode, Encode, MaxEncodedLen, TypeInfo}; use sp_runtime::traits::Saturating; use zeitgeist_primitives::types::OutcomeReport; +/// The original voting outcome owner information. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub enum Possession { + /// The outcome is owned by a single account. + /// This happens due to the call to `add_vote_outcome`. Paid { owner: AccountId, fee: Balance }, + /// The outcome is owned by multiple accounts. + /// When a global dispute is triggered, these are the owners of the initially added outcomes. Shared { owners: Owners }, } @@ -77,6 +82,7 @@ impl } } +/// The current status of the global dispute. #[derive(TypeInfo, Debug, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub enum GdStatus { /// The global dispute is initialized. @@ -91,9 +97,14 @@ pub enum GdStatus { Destroyed, } +/// Relevant details about a reward. pub struct RewardInfo { + /// The market id of the market, which was resolved by a global dispute. pub market_id: MarketId, + /// The amount of the reward. + /// It is equal to the free balance of source. pub reward: Balance, + /// The account which holds the reward. pub source: AccountId, } From acdffeda5c21d80c9e0c799f73041742c878a74b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 08:37:59 +0100 Subject: [PATCH 068/368] use api instead of config parameter --- runtime/common/src/lib.rs | 4 -- .../src/global_disputes_pallet_api.rs | 6 +++ zrml/global-disputes/src/lib.rs | 42 +++++++++++-------- zrml/global-disputes/src/tests.rs | 2 +- zrml/global-disputes/src/types.rs | 2 +- zrml/prediction-markets/src/benchmarks.rs | 7 +++- zrml/prediction-markets/src/lib.rs | 6 --- zrml/prediction-markets/src/mock.rs | 4 -- 8 files changed, 38 insertions(+), 35 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 9623c3c35..c9c8deb35 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1004,10 +1004,6 @@ macro_rules! impl_config_traits { type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; - #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] - type AddOutcomePeriod = AddOutcomePeriod; - #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] - type VotePeriod = VotePeriod; // LiquidityMining is currently unstable. // NoopLiquidityMining will be applied only to mainnet once runtimes are separated. type LiquidityMining = NoopLiquidityMining; diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 0045a9fc6..65c2179cd 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -24,6 +24,12 @@ use zeitgeist_primitives::types::OutcomeReport; /// The trait to initiate and resolve the global disputes. pub trait GlobalDisputesPalletApi { + /// Return the `AddOutcomePeriod` parameter. + fn get_add_outcome_period() -> BlockNumber; + + /// Return the `VotePeriod` parameter. + fn get_vote_period() -> BlockNumber; + /// Push a voting outcome for one global dispute. /// /// # Arguments diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index df259809d..b226233c3 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -675,19 +675,22 @@ mod pallet { } fn update_winner(market_id: &MarketIdOf, outcome: &OutcomeReport, amount: BalanceOf) { - >::mutate(market_id, |highest: &mut Option>| { - *highest = Some(highest.clone().map_or( - GlobalDisputeInfo::new(outcome.clone(), amount), - |mut prev_gd_info| { - if amount >= prev_gd_info.outcome_info.outcome_sum { - prev_gd_info.update_winner(outcome.clone(), amount); - prev_gd_info - } else { - prev_gd_info - } - }, - )); - }); + >::mutate( + market_id, + |highest: &mut Option>| { + *highest = Some(highest.clone().map_or( + GlobalDisputeInfo::new(outcome.clone(), amount), + |mut prev_gd_info| { + if amount >= prev_gd_info.outcome_info.outcome_sum { + prev_gd_info.update_winner(outcome.clone(), amount); + prev_gd_info + } else { + prev_gd_info + } + }, + )); + }, + ); } fn reward_shared_possession( @@ -740,10 +743,7 @@ mod pallet { ExistenceRequirement::AllowDeath, ); // not really much we can do if it fails - debug_assert!( - res.is_ok(), - "Global Disputes: Rewarding a outcome owner failed." - ); + debug_assert!(res.is_ok(), "Global Disputes: Rewarding a outcome owner failed."); Self::deposit_event(Event::OutcomeOwnerRewarded { market_id: reward_info.market_id, owner, @@ -757,6 +757,14 @@ mod pallet { where T: Config, { + fn get_add_outcome_period() -> T::BlockNumber { + T::AddOutcomePeriod::get() + } + + fn get_vote_period() -> T::BlockNumber { + T::VotePeriod::get() + } + fn push_vote_outcome( market_id: &MarketIdOf, outcome: OutcomeReport, diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index eaaf58711..05fa72cfe 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -21,7 +21,7 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, mock::*, - types::{GlobalDisputeInfo, GdStatus, OutcomeInfo, Possession}, + types::{GdStatus, GlobalDisputeInfo, OutcomeInfo, Possession}, utils::market_mock, BalanceOf, Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, }; diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 8ee5eb95f..9e2d829d5 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -23,7 +23,7 @@ use zeitgeist_primitives::types::OutcomeReport; /// The original voting outcome owner information. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub enum Possession { - /// The outcome is owned by a single account. + /// The outcome is owned by a single account. /// This happens due to the call to `add_vote_outcome`. Paid { owner: AccountId, fee: Balance }, /// The outcome is owned by multiple accounts. diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index da80c9afb..c1f5bb68f 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -44,6 +44,8 @@ use zeitgeist_primitives::{ }, }; use zrml_authorized::Pallet as AuthorizedPallet; +#[cfg(feature = "with-global-disputes")] +use zrml_global_disputes::GlobalDisputesPalletApi; use zrml_market_commons::MarketCommonsPalletApi; use frame_support::{traits::Hooks, BoundedVec}; @@ -833,8 +835,9 @@ benchmarks! { #[cfg(feature = "with-global-disputes")] { - let add_outcome_end = current_block + ::AddOutcomePeriod::get(); - let vote_end = add_outcome_end + ::VotePeriod::get(); + let add_outcome_end = current_block + + ::GlobalDisputes::get_add_outcome_period(); + let vote_end = add_outcome_end + ::GlobalDisputes::get_vote_period(); // the complexity depends on MarketIdsPerDisputeBlock at the current block // this is because a variable number of market ids need to be decoded from the storage MarketIdsPerDisputeBlock::::insert(vote_end, market_ids_1); diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 84cf97ad5..fd94ea7fe 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1586,12 +1586,6 @@ mod pallet { Self::BlockNumber, >; - #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] - type AddOutcomePeriod: Get; - - #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] - type VotePeriod: Get; - type LiquidityMining: LiquidityMiningPalletApi< AccountId = Self::AccountId, Balance = BalanceOf, diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 54a8fc7b3..f518c4894 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -152,10 +152,6 @@ impl crate::Config for Runtime { type Event = Event; #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; - #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] - type AddOutcomePeriod = AddOutcomePeriod; - #[cfg(all(feature = "runtime-benchmarks", feature = "with-global-disputes"))] - type VotePeriod = VotePeriod; type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; type MaxDisputes = MaxDisputes; From 41589d887c682f8a42f19266b589e3694b76d743 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 08:49:51 +0100 Subject: [PATCH 069/368] rename Owners to OwnerInfo --- zrml/global-disputes/src/lib.rs | 2 +- zrml/global-disputes/src/types.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index b226233c3..c5986ac20 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -63,6 +63,7 @@ mod pallet { pub type AccountIdOf = ::AccountId; + pub(crate) type OwnerInfoOf = BoundedVec, ::MaxOwners>; pub type OutcomeInfoOf = OutcomeInfo, BalanceOf, OwnerInfoOf>; pub type GlobalDisputeInfoOf = GlobalDisputeInfo< AccountIdOf, @@ -71,7 +72,6 @@ mod pallet { ::BlockNumber, >; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - pub(crate) type OwnerInfoOf = BoundedVec, ::MaxOwners>; pub type LockInfoOf = BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; type RewardInfoOf = RewardInfo, AccountIdOf, BalanceOf>; diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 9e2d829d5..60ceff194 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -22,21 +22,21 @@ use zeitgeist_primitives::types::OutcomeReport; /// The original voting outcome owner information. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub enum Possession { +pub enum Possession { /// The outcome is owned by a single account. /// This happens due to the call to `add_vote_outcome`. Paid { owner: AccountId, fee: Balance }, /// The outcome is owned by multiple accounts. /// When a global dispute is triggered, these are the owners of the initially added outcomes. - Shared { owners: Owners }, + Shared { owners: OwnerInfo }, } -impl Possession { +impl Possession { pub fn is_shared(&self) -> bool { matches!(self, Possession::Shared { .. }) } - pub fn get_shared_owners(self) -> Option { + pub fn get_shared_owners(self) -> Option { match self { Possession::Shared { owners } => Some(owners), _ => None, @@ -50,26 +50,26 @@ impl Possession { /// The information about a voting outcome of a global dispute. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct OutcomeInfo { +pub struct OutcomeInfo { /// The current sum of all locks on this outcome. pub outcome_sum: Balance, /// The information about the owner(s) and optionally additional fee. - pub possession: Option>, + pub possession: Option>, } /// The general information about the global dispute. #[derive(Debug, TypeInfo, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] -pub struct GlobalDisputeInfo { +pub struct GlobalDisputeInfo { /// The outcome which is in the lead. pub winner_outcome: OutcomeReport, /// The information about the winning outcome. - pub outcome_info: OutcomeInfo, + pub outcome_info: OutcomeInfo, /// The current status of the global dispute. pub status: GdStatus, } -impl - GlobalDisputeInfo +impl + GlobalDisputeInfo { pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession: None }; From 7e5ff3c2c2c0b016de54e7ae33b60982c83f9a2e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 09:18:50 +0100 Subject: [PATCH 070/368] rename VotePeriod to GdVotingPeriod --- primitives/src/constants/mock.rs | 2 +- runtime/battery-station/src/parameters.rs | 2 +- runtime/common/src/lib.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/global-disputes/src/benchmarks.rs | 6 +++--- zrml/global-disputes/src/global_disputes_pallet_api.rs | 2 +- zrml/global-disputes/src/lib.rs | 10 +++++----- zrml/global-disputes/src/mock.rs | 4 ++-- zrml/prediction-markets/src/mock.rs | 4 ++-- zrml/prediction-markets/src/tests.rs | 5 ++--- 10 files changed, 19 insertions(+), 20 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 7bd0ebc83..6cfe6860b 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -30,7 +30,7 @@ parameter_types! { pub const MaxOwners: u32 = 10; pub const MinOutcomeVoteAmount: Balance = 10 * CENT; pub const RemoveKeysLimit: u32 = 250; - pub const VotePeriod: BlockNumber = 140; + pub const GdVotingPeriod: BlockNumber = 140; pub const VotingOutcomeFee: Balance = 100 * CENT; } diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 396ece2c0..248b98d5b 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -369,7 +369,7 @@ parameter_types! { /// The minimum required amount to vote on an outcome. pub const MinOutcomeVoteAmount: Balance = 10 * BASE; /// The time period in which votes are allowed. - pub const VotePeriod: BlockNumber = 3 * BLOCKS_PER_DAY; + pub const GdVotingPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The fee required to add a voting outcome. pub const VotingOutcomeFee: Balance = 200 * BASE; /// The remove limit for the Outcomes storage double map. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index c9c8deb35..b971b5f60 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1075,7 +1075,7 @@ macro_rules! impl_config_traits { type MaxOwners = MaxOwners; type MinOutcomeVoteAmount = MinOutcomeVoteAmount; type RemoveKeysLimit = RemoveKeysLimit; - type VotePeriod = VotePeriod; + type GdVotingPeriod = GdVotingPeriod; type VotingOutcomeFee = VotingOutcomeFee; type WeightInfo = zrml_global_disputes::weights::WeightInfo; } diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index afa25bdd7..cae9ae446 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -369,7 +369,7 @@ parameter_types! { /// The minimum required amount to vote on an outcome. pub const MinOutcomeVoteAmount: Balance = 10 * BASE; /// The time period in which votes are allowed. - pub const VotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const GdVotingPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The fee required to add a voting outcome. pub const VotingOutcomeFee: Balance = 200 * BASE; /// The remove limit for the Outcomes storage double map. diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index 973addd58..aa7aa507d 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -102,7 +102,7 @@ benchmarks! { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); - let vote_end = add_outcome_end + T::VotePeriod::get(); + let vote_end = add_outcome_end + T::GdVotingPeriod::get(); let gd_info = GlobalDisputeInfo { winner_outcome: outcome.clone(), status: GdStatus::Active { add_outcome_end, vote_end }, @@ -141,7 +141,7 @@ benchmarks! { // because we need `lock_needed` to be greater zero to set a lock. let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); - let vote_end = add_outcome_end + T::VotePeriod::get(); + let vote_end = add_outcome_end + T::GdVotingPeriod::get(); let gd_info = GlobalDisputeInfo { winner_outcome: outcome, status: GdStatus::Active { add_outcome_end, vote_end }, @@ -234,7 +234,7 @@ benchmarks! { let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession: None }; let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); - let vote_end = add_outcome_end + T::VotePeriod::get(); + let vote_end = add_outcome_end + T::GdVotingPeriod::get(); let gd_info = GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(0), status: GdStatus::Active { add_outcome_end, vote_end }, diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 65c2179cd..de7aa479c 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -27,7 +27,7 @@ pub trait GlobalDisputesPalletApi { /// Return the `AddOutcomePeriod` parameter. fn get_add_outcome_period() -> BlockNumber; - /// Return the `VotePeriod` parameter. + /// Return the `GdVotingPeriod` parameter. fn get_vote_period() -> BlockNumber; /// Push a voting outcome for one global dispute. diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index c5986ac20..0c641e984 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -136,7 +136,7 @@ mod pallet { /// The time period in which votes are allowed. #[pallet::constant] - type VotePeriod: Get; + type GdVotingPeriod: Get; /// The fee required to add a voting outcome. #[pallet::constant] @@ -248,7 +248,7 @@ mod pallet { /// The period in which outcomes can be added is over. AddOutcomePeriodIsOver, /// It is not inside the period in which votes are allowed. - NotInVotePeriod, + NotInGdVotingPeriod, /// The operation requires a global dispute in a destroyed state. GlobalDisputeNotDestroyed, } @@ -519,7 +519,7 @@ mod pallet { .ok_or(Error::::NoGlobalDisputeInitialized)?; let now = >::block_number(); if let GdStatus::Active { add_outcome_end, vote_end } = gd_info.status { - ensure!(add_outcome_end < now && now <= vote_end, Error::::NotInVotePeriod); + ensure!(add_outcome_end < now && now <= vote_end, Error::::NotInGdVotingPeriod); } else { return Err(Error::::InvalidGlobalDisputeStatus.into()); } @@ -762,7 +762,7 @@ mod pallet { } fn get_vote_period() -> T::BlockNumber { - T::VotePeriod::get() + T::GdVotingPeriod::get() } fn push_vote_outcome( @@ -863,7 +863,7 @@ mod pallet { let now = >::block_number(); let add_outcome_end = now.saturating_add(T::AddOutcomePeriod::get()); - let vote_end = add_outcome_end.saturating_add(T::VotePeriod::get()); + let vote_end = add_outcome_end.saturating_add(T::GdVotingPeriod::get()); let ids_len = T::DisputeResolution::add_auto_resolve(market_id, vote_end)?; >::try_mutate(market_id, |gd_info| -> DispatchResult { diff --git a/zrml/global-disputes/src/mock.rs b/zrml/global-disputes/src/mock.rs index f389d6bf6..4b1080664 100644 --- a/zrml/global-disputes/src/mock.rs +++ b/zrml/global-disputes/src/mock.rs @@ -33,7 +33,7 @@ use sp_runtime::{ use zeitgeist_primitives::{ constants::mock::{ AddOutcomePeriod, BlockHashCount, GlobalDisputeLockId, GlobalDisputesPalletId, MaxReserves, - MinOutcomeVoteAmount, MinimumPeriod, PmPalletId, RemoveKeysLimit, VotePeriod, + MinOutcomeVoteAmount, MinimumPeriod, PmPalletId, RemoveKeysLimit, GdVotingPeriod, VotingOutcomeFee, BASE, }, traits::DisputeResolutionApi, @@ -128,7 +128,7 @@ impl crate::Config for Runtime { type MaxOwners = MaxOwners; type MinOutcomeVoteAmount = MinOutcomeVoteAmount; type RemoveKeysLimit = RemoveKeysLimit; - type VotePeriod = VotePeriod; + type GdVotingPeriod = GdVotingPeriod; type VotingOutcomeFee = VotingOutcomeFee; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index f518c4894..9d5df2101 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -57,7 +57,7 @@ use zeitgeist_primitives::{ #[cfg(feature = "with-global-disputes")] use zeitgeist_primitives::constants::mock::{ AddOutcomePeriod, GlobalDisputeLockId, GlobalDisputesPalletId, MaxGlobalDisputeVotes, - MaxOwners, MinOutcomeVoteAmount, RemoveKeysLimit, VotePeriod, VotingOutcomeFee, + MaxOwners, MinOutcomeVoteAmount, RemoveKeysLimit, GdVotingPeriod, VotingOutcomeFee, }; use zrml_rikiddo::types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}; @@ -336,7 +336,7 @@ impl zrml_global_disputes::Config for Runtime { type MaxOwners = MaxOwners; type MinOutcomeVoteAmount = MinOutcomeVoteAmount; type RemoveKeysLimit = RemoveKeysLimit; - type VotePeriod = VotePeriod; + type GdVotingPeriod = GdVotingPeriod; type VotingOutcomeFee = VotingOutcomeFee; type WeightInfo = zrml_global_disputes::weights::WeightInfo; } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index df1fb7bb4..f218fcd6a 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3027,7 +3027,6 @@ fn start_global_dispute_works() { #[cfg(feature = "with-global-disputes")] { - use zeitgeist_primitives::constants::mock::{AddOutcomePeriod, VotePeriod}; use zrml_global_disputes::GlobalDisputesPalletApi; let now = >::block_number(); @@ -3053,8 +3052,8 @@ fn start_global_dispute_works() { let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); assert_eq!(removable_market_ids.len(), 0); - let add_outcome_end = now + AddOutcomePeriod::get(); - let vote_end = add_outcome_end + VotePeriod::get(); + let add_outcome_end = now + GlobalDisputes::get_add_outcome_period(); + let vote_end = add_outcome_end + GlobalDisputes::get_vote_period(); let market_ids = MarketIdsPerDisputeBlock::::get(vote_end); assert_eq!(market_ids, vec![market_id]); assert!(GlobalDisputes::is_unfinished(&market_id)); From 83f79faaf34ebc92440ecfb77ce8c44afee85969 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 09:34:18 +0100 Subject: [PATCH 071/368] remove unused methods --- zrml/global-disputes/src/mock.rs | 6 +++--- zrml/global-disputes/src/types.rs | 8 -------- zrml/prediction-markets/src/mock.rs | 4 ++-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/zrml/global-disputes/src/mock.rs b/zrml/global-disputes/src/mock.rs index 4b1080664..da5bcccaa 100644 --- a/zrml/global-disputes/src/mock.rs +++ b/zrml/global-disputes/src/mock.rs @@ -32,9 +32,9 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - AddOutcomePeriod, BlockHashCount, GlobalDisputeLockId, GlobalDisputesPalletId, MaxReserves, - MinOutcomeVoteAmount, MinimumPeriod, PmPalletId, RemoveKeysLimit, GdVotingPeriod, - VotingOutcomeFee, BASE, + AddOutcomePeriod, BlockHashCount, GdVotingPeriod, GlobalDisputeLockId, + GlobalDisputesPalletId, MaxReserves, MinOutcomeVoteAmount, MinimumPeriod, PmPalletId, + RemoveKeysLimit, VotingOutcomeFee, BASE, }, traits::DisputeResolutionApi, types::{ diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 60ceff194..ed97d4e7f 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -32,20 +32,12 @@ pub enum Possession { } impl Possession { - pub fn is_shared(&self) -> bool { - matches!(self, Possession::Shared { .. }) - } - pub fn get_shared_owners(self) -> Option { match self { Possession::Shared { owners } => Some(owners), _ => None, } } - - pub fn is_paid(&self) -> bool { - matches!(self, Possession::Paid { .. }) - } } /// The information about a voting outcome of a global dispute. diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 9d5df2101..2fe918083 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -56,8 +56,8 @@ use zeitgeist_primitives::{ #[cfg(feature = "with-global-disputes")] use zeitgeist_primitives::constants::mock::{ - AddOutcomePeriod, GlobalDisputeLockId, GlobalDisputesPalletId, MaxGlobalDisputeVotes, - MaxOwners, MinOutcomeVoteAmount, RemoveKeysLimit, GdVotingPeriod, VotingOutcomeFee, + AddOutcomePeriod, GdVotingPeriod, GlobalDisputeLockId, GlobalDisputesPalletId, + MaxGlobalDisputeVotes, MaxOwners, MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, }; use zrml_rikiddo::types::{EmaMarketVolume, FeeSigmoid, RikiddoSigmoidMV}; From f67e92bad518fb8136d557e71dc5c4644c13ffad Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 09:45:38 +0100 Subject: [PATCH 072/368] remove struct --- zrml/global-disputes/src/lib.rs | 50 +++++++++++++++---------------- zrml/global-disputes/src/types.rs | 11 ------- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 0c641e984..68debb497 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -74,7 +74,6 @@ mod pallet { type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub type LockInfoOf = BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; - type RewardInfoOf = RewardInfo, AccountIdOf, BalanceOf>; // TODO(#968): to remove after the storage migration pub type WinnerInfoOf = OldWinnerInfo, OwnerInfoOf>; @@ -469,19 +468,19 @@ mod pallet { let reward_account_free_balance = T::Currency::free_balance(&reward_account); ensure!(!reward_account_free_balance.is_zero(), Error::::NoFundsToReward); - let reward_info = RewardInfo { - market_id, - reward: reward_account_free_balance, - source: reward_account, - }; - match gd_info.outcome_info.possession { - Some(Possession::Shared { owners }) => { - Self::reward_shared_possession(reward_info, owners) - } - Some(Possession::Paid { owner, fee: _ }) => { - Self::reward_paid_possession(reward_info, owner) - } + Some(Possession::Shared { owners }) => Self::reward_shared_possession( + market_id, + reward_account, + reward_account_free_balance, + owners, + ), + Some(Possession::Paid { owner, fee: _ }) => Self::reward_paid_possession( + market_id, + reward_account, + reward_account_free_balance, + owner, + ), None => Err(Error::::NoPossession.into()), } } @@ -694,20 +693,22 @@ mod pallet { } fn reward_shared_possession( - reward_info: RewardInfoOf, + market_id: MarketIdOf, + reward_account: AccountIdOf, + reward: BalanceOf, owners: OwnerInfoOf, ) -> DispatchResultWithPostInfo { - let mut remainder = reward_info.reward; + let mut remainder = reward; let owners_len = owners.len() as u32; let owners_len_in_balance: BalanceOf = >::from(owners_len); - if let Some(reward_per_each) = reward_info.reward.checked_div(&owners_len_in_balance) { + if let Some(reward_per_each) = reward.checked_div(&owners_len_in_balance) { for winner in owners.iter() { // *Should* always be equal to `reward_per_each` let reward = remainder.min(reward_per_each); remainder = remainder.saturating_sub(reward); // Reward the losing funds to the winners let res = T::Currency::transfer( - &reward_info.source, + &reward_account, winner, reward, ExistenceRequirement::AllowDeath, @@ -726,28 +727,27 @@ mod pallet { debug_assert!(false); } Self::deposit_event(Event::OutcomeOwnersRewarded { - market_id: reward_info.market_id, + market_id, owners: owners.into_inner(), }); Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(owners_len))).into()) } fn reward_paid_possession( - reward_info: RewardInfoOf, + market_id: MarketIdOf, + reward_account: AccountIdOf, + reward: BalanceOf, owner: AccountIdOf, ) -> DispatchResultWithPostInfo { let res = T::Currency::transfer( - &reward_info.source, + &reward_account, &owner, - reward_info.reward, + reward, ExistenceRequirement::AllowDeath, ); // not really much we can do if it fails debug_assert!(res.is_ok(), "Global Disputes: Rewarding a outcome owner failed."); - Self::deposit_event(Event::OutcomeOwnerRewarded { - market_id: reward_info.market_id, - owner, - }); + Self::deposit_event(Event::OutcomeOwnerRewarded { market_id, owner }); Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(1u32))).into()) } } diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index ed97d4e7f..5691b72e7 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -89,17 +89,6 @@ pub enum GdStatus { Destroyed, } -/// Relevant details about a reward. -pub struct RewardInfo { - /// The market id of the market, which was resolved by a global dispute. - pub market_id: MarketId, - /// The amount of the reward. - /// It is equal to the free balance of source. - pub reward: Balance, - /// The account which holds the reward. - pub source: AccountId, -} - // TODO(#986): to remove after the storage migration /// The information about a voting outcome of a global dispute. From 0f20fb0125f2ed2fc74bc9890676d23af47e904e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 09:54:39 +0100 Subject: [PATCH 073/368] improve readability --- zrml/global-disputes/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 5691b72e7..06cc2c232 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -79,7 +79,7 @@ impl pub enum GdStatus { /// The global dispute is initialized. Initialized, - /// The global dispute is in progress. Save the addition of outcome end and vote end. + /// The global dispute is in progress. /// The block number `add_outcome_end`, when the addition of new outcomes is over. /// The block number `vote_end`, when the global dispute voting period is over. Active { add_outcome_end: BlockNumber, vote_end: BlockNumber }, From 0d0d1f80917f4bcc588b1596dd171f6a9d5c47be Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 11:52:58 +0100 Subject: [PATCH 074/368] make possession non-optional --- zrml/global-disputes/src/benchmarks.rs | 25 ++++++------ zrml/global-disputes/src/lib.rs | 55 +++++++++++++------------- zrml/global-disputes/src/migrations.rs | 29 ++++++-------- zrml/global-disputes/src/tests.rs | 29 ++++++-------- zrml/global-disputes/src/types.rs | 10 +++-- 5 files changed, 73 insertions(+), 75 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index aa7aa507d..d41c902db 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -98,7 +98,7 @@ benchmarks! { // minus one to ensure, that we use the worst case // for using a new winner info after the vote_on_outcome call let vote_sum = amount - 1u128.saturated_into(); - let possession = Some(Possession::Shared { owners: Default::default() }); + let possession = Possession::Shared { owners: Default::default() }; let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); @@ -135,7 +135,7 @@ benchmarks! { } let owners = BoundedVec::try_from(owners).unwrap(); let outcome = OutcomeReport::Scalar(0); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; // is_finished is false, // because we need `lock_needed` to be greater zero to set a lock. @@ -183,7 +183,7 @@ benchmarks! { } let owners = BoundedVec::try_from(owners).unwrap(); let outcome = OutcomeReport::Scalar(0); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; // is_finished is true, // because we need `lock_needed` to be zero to remove all locks. @@ -231,7 +231,8 @@ benchmarks! { let owners: BoundedVec, T::MaxOwners> = BoundedVec::try_from(owners) .unwrap(); - let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession: None }; + let possession = Possession::Shared { owners }; + let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession: possession.clone() }; let now = >::block_number(); let add_outcome_end = now + T::AddOutcomePeriod::get(); let vote_end = add_outcome_end + T::GdVotingPeriod::get(); @@ -258,12 +259,12 @@ benchmarks! { let gd_info = >::get(market_id).unwrap(); assert_eq!(gd_info.outcome_info.outcome_sum, T::VotingOutcomeFee::get()); // None as long as dispute not finished and reward_outcome_owner not happened - assert_eq!(gd_info.outcome_info.possession, None); + assert_eq!(gd_info.outcome_info.possession, possession); let outcomes_item = >::get(market_id, outcome).unwrap(); assert_eq!(outcomes_item.outcome_sum, T::VotingOutcomeFee::get()); assert_eq!( - outcomes_item.possession.unwrap(), + outcomes_item.possession, Possession::Paid { owner: caller, fee: T::VotingOutcomeFee::get() }, ); } @@ -279,7 +280,7 @@ benchmarks! { owners_vec.push(owner); } let owners = BoundedVec::try_from(owners_vec.clone()).unwrap(); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let gd_info = GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(0), status: GdStatus::Finished, @@ -309,7 +310,7 @@ benchmarks! { ) .unwrap(); } verify { - assert!(gd_info.outcome_info.possession.unwrap().get_shared_owners().unwrap().len() == o as usize); + assert!(gd_info.outcome_info.possession.get_shared_owners().unwrap().len() == o as usize); assert_last_event::( Event::OutcomeOwnersRewarded:: { market_id, @@ -355,14 +356,14 @@ benchmarks! { let owners = BoundedVec::try_from(owners.clone()).unwrap(); let winner_outcome = OutcomeReport::Scalar(0); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession, }; >::insert(market_id, winner_outcome.clone(), outcome_info); - let possession = Some(Possession::Shared { owners: Default::default() }); + let possession = Possession::Shared { owners: Default::default() }; let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession, @@ -410,14 +411,14 @@ benchmarks! { let owners = BoundedVec::try_from(owners.clone()).unwrap(); let winner_outcome = OutcomeReport::Scalar(0); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession, }; >::insert(market_id, winner_outcome.clone(), outcome_info); - let possession = Some(Possession::Shared { owners: Default::default() }); + let possession = Possession::Shared { owners: Default::default() }; let outcome_info = OutcomeInfo { outcome_sum: 42u128.saturated_into(), possession, diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 68debb497..7a897d39d 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -71,6 +71,7 @@ mod pallet { OwnerInfoOf, ::BlockNumber, >; + pub type PossessionOf = Possession, BalanceOf, OwnerInfoOf>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub type LockInfoOf = BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; @@ -230,8 +231,6 @@ mod pallet { NoGlobalDisputeInitialized, /// The global dispute has to be in initialized state during the initial outcome setup. NotInitialized, - /// There is no owner information for this outcome. - NoPossession, /// The voting outcome has been already added. OutcomeAlreadyExists, /// The outcome specified is not present in the voting outcomes. @@ -304,10 +303,9 @@ mod pallet { ExistenceRequirement::AllowDeath, )?; - Self::update_winner(&market_id, &outcome, voting_outcome_fee); - let possession = - Some(Possession::Paid { owner: owner.clone(), fee: voting_outcome_fee }); + let possession = Possession::Paid { owner: owner.clone(), fee: voting_outcome_fee }; let outcome_info = OutcomeInfo { outcome_sum: voting_outcome_fee, possession }; + Self::update_winner(&market_id, &outcome, outcome_info.clone()); >::insert(market_id, outcome.clone(), outcome_info); Self::deposit_event(Event::AddedVotingOutcome { market_id, owner, outcome }); @@ -348,7 +346,7 @@ mod pallet { >::drain_prefix(market_id).take(T::RemoveKeysLimit::get() as usize) { match outcome_info.possession { - Some(Possession::Paid { owner, fee }) => { + Possession::Paid { owner, fee } => { T::Currency::transfer( &Self::reward_account(&market_id), &owner, @@ -356,10 +354,9 @@ mod pallet { ExistenceRequirement::AllowDeath, )?; } - Some(Possession::Shared { owners }) => { + Possession::Shared { owners } => { owners_len = owners_len.saturating_add(owners.len() as u32); } - None => (), } removed_keys_amount = removed_keys_amount.saturating_add(1u32); } @@ -405,7 +402,7 @@ mod pallet { let mut owners_len = 0u32; // move the winning outcome info to GlobalDisputesInfo before it gets drained if let Some(outcome_info) = winning_outcome { - if let Some(Possession::Shared { owners }) = &outcome_info.possession { + if let Possession::Shared { owners } = &outcome_info.possession { owners_len = owners.len() as u32; } // storage write is needed here in case, @@ -421,7 +418,7 @@ mod pallet { for (_, outcome_info) in >::drain_prefix(market_id).take(T::RemoveKeysLimit::get() as usize) { - if let Some(Possession::Shared { owners }) = outcome_info.possession { + if let Possession::Shared { owners } = outcome_info.possession { owners_len = owners_len.saturating_add(owners.len() as u32); } removed_keys_amount = removed_keys_amount.saturating_add(1u32); @@ -469,19 +466,18 @@ mod pallet { ensure!(!reward_account_free_balance.is_zero(), Error::::NoFundsToReward); match gd_info.outcome_info.possession { - Some(Possession::Shared { owners }) => Self::reward_shared_possession( + Possession::Shared { owners } => Self::reward_shared_possession( market_id, reward_account, reward_account_free_balance, owners, ), - Some(Possession::Paid { owner, fee: _ }) => Self::reward_paid_possession( + Possession::Paid { owner, fee: _ } => Self::reward_paid_possession( market_id, reward_account, reward_account_free_balance, owner, ), - None => Err(Error::::NoPossession.into()), } } @@ -526,9 +522,8 @@ mod pallet { let mut outcome_info = >::get(market_id, &outcome).ok_or(Error::::OutcomeDoesNotExist)?; let outcome_owners_len = match outcome_info.possession { - Some(Possession::Shared { ref owners }) => owners.len() as u32, - Some(Possession::Paid { .. }) => 1u32, - None => 0u32, + Possession::Shared { ref owners } => owners.len() as u32, + Possession::Paid { .. } => 1u32, }; // The `outcome_sum` never decreases (only increases) to allow @@ -538,7 +533,7 @@ mod pallet { // than the second highest `outcome_sum`. let add_to_outcome_sum = |a| { outcome_info.outcome_sum = outcome_info.outcome_sum.saturating_add(a); - Self::update_winner(&market_id, &outcome, outcome_info.outcome_sum); + Self::update_winner(&market_id, &outcome, outcome_info.clone()); >::insert(market_id, &outcome, outcome_info); }; @@ -673,12 +668,17 @@ mod pallet { T::GlobalDisputesPalletId::get().into_sub_account_truncating(market_id) } - fn update_winner(market_id: &MarketIdOf, outcome: &OutcomeReport, amount: BalanceOf) { + fn update_winner( + market_id: &MarketIdOf, + outcome: &OutcomeReport, + outcome_info: OutcomeInfoOf, + ) { + let amount = outcome_info.outcome_sum; >::mutate( market_id, |highest: &mut Option>| { *highest = Some(highest.clone().map_or( - GlobalDisputeInfo::new(outcome.clone(), amount), + GlobalDisputeInfo::new(outcome.clone(), outcome_info.possession, amount), |mut prev_gd_info| { if amount >= prev_gd_info.outcome_info.outcome_sum { prev_gd_info.update_winner(outcome.clone(), amount); @@ -782,22 +782,22 @@ mod pallet { Some(mut outcome_info) => { let outcome_sum = outcome_info.outcome_sum.saturating_add(initial_vote_balance); outcome_info.outcome_sum = outcome_sum; - let possession = outcome_info.possession.ok_or(Error::::NoPossession)?; - let mut owners = possession + let mut owners = outcome_info + .possession .get_shared_owners() .ok_or(Error::::SharedPossessionRequired)?; owners.try_push(owner.clone()).map_err(|_| Error::::MaxOwnersReached)?; - outcome_info.possession = Some(Possession::Shared { owners }); - Self::update_winner(market_id, &outcome, outcome_sum); + outcome_info.possession = Possession::Shared { owners }; + Self::update_winner(market_id, &outcome, outcome_info.clone()); >::insert(market_id, outcome, outcome_info); } None => { // adding one item to BoundedVec can not fail if let Ok(owners) = BoundedVec::try_from(vec![owner.clone()]) { - Self::update_winner(market_id, &outcome, initial_vote_balance); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let outcome_info = OutcomeInfo { outcome_sum: initial_vote_balance, possession }; + Self::update_winner(market_id, &outcome, outcome_info.clone()); >::insert(market_id, outcome, outcome_info); } else { log::error!("Global Disputes: Could not construct a bounded vector."); @@ -814,11 +814,10 @@ mod pallet { ) -> Option<(BalanceOf, Vec>)> { >::get(market_id, outcome).map(|outcome_info| { match outcome_info.possession { - Some(Possession::Shared { owners }) => { + Possession::Shared { owners } => { (outcome_info.outcome_sum, owners.into_inner()) } - Some(Possession::Paid { owner, .. }) => (outcome_info.outcome_sum, vec![owner]), - None => (outcome_info.outcome_sum, vec![]), + Possession::Paid { owner, .. } => (outcome_info.outcome_sum, vec![owner]), } }) } diff --git a/zrml/global-disputes/src/migrations.rs b/zrml/global-disputes/src/migrations.rs index 80d0b5e43..67a538e0e 100644 --- a/zrml/global-disputes/src/migrations.rs +++ b/zrml/global-disputes/src/migrations.rs @@ -62,15 +62,14 @@ impl OnRuntimeUpgrade let owners = winner_info.outcome_info.owners; let owners_len = owners.len(); let possession = match owners_len { - 0usize => None, - 1usize => Some(Possession::Paid { + 1usize => Possession::Paid { owner: owners .get(0) .expect("Owners len is 1, but could not get this owner.") .clone(), fee: T::VotingOutcomeFee::get(), - }), - _ => Some(Possession::Shared { owners }), + }, + _ => Possession::Shared { owners }, }; let outcome_info = @@ -93,15 +92,14 @@ impl OnRuntimeUpgrade let owners = old_value.owners; let owners_len = owners.len(); let possession = match owners_len { - 0usize => None, - 1usize => Some(Possession::Paid { + 1usize => Possession::Paid { owner: owners .get(0) .expect("Owners len is 1, but could not get this owner.") .clone(), fee: T::VotingOutcomeFee::get(), - }), - _ => Some(Possession::Shared { owners }), + }, + _ => Possession::Shared { owners }, }; let new_value = OutcomeInfo { outcome_sum: old_value.outcome_sum, possession }; @@ -156,15 +154,14 @@ impl OnRuntimeUpgrade let owners_len = owners.len(); let possession = match owners_len { - 0usize => None, - 1usize => Some(Possession::Paid { + 1usize => Possession::Paid { owner: owners .get(0) .expect("Owners len is 1, but could not get this owner.") .clone(), fee: T::VotingOutcomeFee::get(), - }), - _ => Some(Possession::Shared { owners }), + }, + _ => Possession::Shared { owners }, }; let outcome_info_expected = @@ -231,7 +228,7 @@ mod tests { ModifyGlobalDisputesStructures::::on_runtime_upgrade(); - let possession = Some(Possession::Paid { owner: ALICE, fee: VotingOutcomeFee::get() }); + let possession = Possession::Paid { owner: ALICE, fee: VotingOutcomeFee::get() }; let new_outcome_info = OutcomeInfo { outcome_sum, possession }; @@ -267,7 +264,7 @@ mod tests { ModifyGlobalDisputesStructures::::on_runtime_upgrade(); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let new_outcome_info = OutcomeInfo { outcome_sum, possession }; @@ -307,7 +304,7 @@ mod tests { ModifyGlobalDisputesStructures::::on_runtime_upgrade(); - let possession = Some(Possession::Shared { owners }); + let possession = Possession::Shared { owners }; let expected = OutcomeInfo { outcome_sum, possession }; let actual = frame_support::migration::get_storage_value::>( @@ -343,7 +340,7 @@ mod tests { ModifyGlobalDisputesStructures::::on_runtime_upgrade(); - let possession = Some(Possession::Paid { owner: ALICE, fee: VotingOutcomeFee::get() }); + let possession = Possession::Paid { owner: ALICE, fee: VotingOutcomeFee::get() }; let expected = OutcomeInfo { outcome_sum, possession }; let actual = frame_support::migration::get_storage_value::>( diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 05fa72cfe..ac436b57c 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -75,9 +75,7 @@ fn check_outcome_sum( >::get(market_id, outcome).unwrap(), OutcomeInfo { outcome_sum: SETUP_AMOUNT + post_setup_amount, - possession: Some(Possession::Shared { - owners: BoundedVec::try_from(vec![ALICE]).unwrap() - }) + possession: Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap() } } ); } @@ -179,7 +177,8 @@ fn add_vote_outcome_fails_if_global_dispute_finished() { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); - let mut gd_info = GlobalDisputeInfo::new(OutcomeReport::Scalar(0), 10 * BASE); + let possession = Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap() }; + let mut gd_info = GlobalDisputeInfo::new(OutcomeReport::Scalar(0), possession, 10 * BASE); gd_info.status = GdStatus::Finished; >::insert(market_id, gd_info); @@ -209,7 +208,7 @@ fn add_vote_outcome_fails_if_outcome_already_exists() { OutcomeReport::Scalar(20), OutcomeInfo { outcome_sum: Zero::zero(), - possession: Some(Possession::Shared { owners: Default::default() }), + possession: Possession::Shared { owners: Default::default() }, }, ); assert_noop!( @@ -253,9 +252,9 @@ fn reward_outcome_owner_works_for_multiple_owners() { OutcomeReport::Scalar(20), OutcomeInfo { outcome_sum: Zero::zero(), - possession: Some(Possession::Shared { + possession: Possession::Shared { owners: BoundedVec::try_from(vec![ALICE, BOB, CHARLIE]).unwrap(), - }), + }, }, ); let _ = Balances::deposit_creating( @@ -267,7 +266,7 @@ fn reward_outcome_owner_works_for_multiple_owners() { status: GdStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, - possession: Some(Possession::Shared { owners: Default::default() }), + possession: Possession::Shared { owners: Default::default() }, }, }; >::insert(market_id, gd_info); @@ -312,10 +311,10 @@ fn reward_outcome_owner_has_dust() { OutcomeReport::Scalar(20), OutcomeInfo { outcome_sum: Zero::zero(), - possession: Some(Possession::Shared { + possession: Possession::Shared { owners: BoundedVec::try_from(vec![ALICE, BOB, CHARLIE, EVE, POOR_PAUL, DAVE]) .unwrap(), - }), + }, }, ); let _ = Balances::deposit_creating(&GlobalDisputes::reward_account(&market_id), 100 * BASE); @@ -324,7 +323,7 @@ fn reward_outcome_owner_has_dust() { status: GdStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, - possession: Some(Possession::Shared { owners: Default::default() }), + possession: Possession::Shared { owners: Default::default() }, }, }; >::insert(market_id, gd_info); @@ -348,9 +347,9 @@ fn reward_outcome_owner_works_for_one_owner() { OutcomeReport::Scalar(20), OutcomeInfo { outcome_sum: Zero::zero(), - possession: Some(Possession::Shared { + possession: Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap(), - }), + }, }, ); let _ = Balances::deposit_creating( @@ -362,9 +361,7 @@ fn reward_outcome_owner_works_for_one_owner() { status: GdStatus::Finished, outcome_info: OutcomeInfo { outcome_sum: 10 * BASE, - possession: Some(Possession::Shared { - owners: BoundedVec::try_from(vec![]).unwrap(), - }), + possession: Possession::Shared { owners: BoundedVec::try_from(vec![]).unwrap() }, }, }; >::insert(market_id, gd_info); diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 06cc2c232..85992bd9f 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -46,7 +46,7 @@ pub struct OutcomeInfo { /// The current sum of all locks on this outcome. pub outcome_sum: Balance, /// The information about the owner(s) and optionally additional fee. - pub possession: Option>, + pub possession: Possession, } /// The general information about the global dispute. @@ -63,8 +63,12 @@ pub struct GlobalDisputeInfo { impl GlobalDisputeInfo { - pub fn new(outcome: OutcomeReport, vote_sum: Balance) -> Self { - let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession: None }; + pub fn new( + outcome: OutcomeReport, + possession: Possession, + vote_sum: Balance, + ) -> Self { + let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; GlobalDisputeInfo { winner_outcome: outcome, status: GdStatus::Initialized, outcome_info } } From 2e0f92b2f164c31be658ed4d20ee9fcc6c6d46f9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 12:28:27 +0100 Subject: [PATCH 075/368] remove get_voting_outcome_info --- zrml/global-disputes/README.md | 2 -- .../src/global_disputes_pallet_api.rs | 17 ------------- zrml/global-disputes/src/lib.rs | 18 +++---------- zrml/prediction-markets/src/tests.rs | 25 ++++++++++++++----- 4 files changed, 22 insertions(+), 40 deletions(-) diff --git a/zrml/global-disputes/README.md b/zrml/global-disputes/README.md index 27fef1ac2..8417e2b42 100644 --- a/zrml/global-disputes/README.md +++ b/zrml/global-disputes/README.md @@ -39,8 +39,6 @@ on which the market finally resolves. - `push_vote_outcome` - Add an initial voting outcome and vote on it with `initial_vote_balance`. -- `get_voting_outcome_info` - Get the information (`outcome_sum`, `owners`) of a - voting outcome. - `determine_voting_winner` - Determine the canonical voting outcome based on total locked tokens. - `does_exist` - Check if the global dispute does already exist. diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index de7aa479c..4176233d7 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -18,7 +18,6 @@ extern crate alloc; -use alloc::vec::Vec; use sp_runtime::{DispatchError, DispatchResult}; use zeitgeist_primitives::types::OutcomeReport; @@ -44,22 +43,6 @@ pub trait GlobalDisputesPalletApi { initial_vote_balance: Balance, ) -> DispatchResult; - /// Get the information about a voting outcome for a global dispute. - /// - /// # Arguments - /// - `market_id` - The id of the market. - /// - `outcome` - The voting outcome to get. - /// - /// # Returns - /// - /// Returns the information stored for a particular outcome. - /// - outcome_sum - The current sum of all locks on this outcome. - /// - owners - The vector of owners of the outcome. - fn get_voting_outcome_info( - market_id: &MarketId, - outcome: &OutcomeReport, - ) -> Option<(Balance, Vec)>; - /// Determine the winner of a global dispute. /// /// # Arguments diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 7a897d39d..1ad427b33 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -31,6 +31,8 @@ pub mod weights; pub use global_disputes_pallet_api::GlobalDisputesPalletApi; pub use pallet::*; +pub type PossessionOf = crate::types::Possession, BalanceOf, OwnerInfoOf>; + #[frame_support::pallet] mod pallet { use crate::{types::*, weights::WeightInfoZeitgeist, GlobalDisputesPalletApi}; @@ -71,7 +73,7 @@ mod pallet { OwnerInfoOf, ::BlockNumber, >; - pub type PossessionOf = Possession, BalanceOf, OwnerInfoOf>; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub type LockInfoOf = BoundedVec<(MarketIdOf, BalanceOf), ::MaxGlobalDisputeVotes>; @@ -808,20 +810,6 @@ mod pallet { Ok(()) } - fn get_voting_outcome_info( - market_id: &MarketIdOf, - outcome: &OutcomeReport, - ) -> Option<(BalanceOf, Vec>)> { - >::get(market_id, outcome).map(|outcome_info| { - match outcome_info.possession { - Possession::Shared { owners } => { - (outcome_info.outcome_sum, owners.into_inner()) - } - Possession::Paid { owner, .. } => (outcome_info.outcome_sum, vec![owner]), - } - }) - } - fn determine_voting_winner(market_id: &MarketIdOf) -> Option { match >::get(market_id) { Some(mut gd_info) => { diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index f218fcd6a..0e21f7d63 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3027,24 +3027,37 @@ fn start_global_dispute_works() { #[cfg(feature = "with-global-disputes")] { - use zrml_global_disputes::GlobalDisputesPalletApi; + use zrml_global_disputes::{ + types::{OutcomeInfo, Possession}, + GlobalDisputesPalletApi, Outcomes, PossessionOf, + }; let now = >::block_number(); assert_ok!(PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id)); // report check + let possession: PossessionOf = Possession::Shared { + owners: frame_support::BoundedVec::try_from(vec![BOB]).unwrap(), + }; + let outcome_info = OutcomeInfo { outcome_sum: Zero::zero(), possession }; assert_eq!( - GlobalDisputes::get_voting_outcome_info(&market_id, &OutcomeReport::Categorical(0)), - Some((Zero::zero(), vec![BOB])), + Outcomes::::get(&market_id, &OutcomeReport::Categorical(0)).unwrap(), + outcome_info ); + let possession: PossessionOf = Possession::Shared { + owners: frame_support::BoundedVec::try_from(vec![CHARLIE]).unwrap(), + }; for i in 1..=::MaxDisputes::get() { let dispute_bond = crate::default_dispute_bond::((i - 1).into()); + let outcome_info = + OutcomeInfo { outcome_sum: dispute_bond, possession: possession.clone() }; assert_eq!( - GlobalDisputes::get_voting_outcome_info( + Outcomes::::get( &market_id, &OutcomeReport::Categorical(i.saturated_into()) - ), - Some((dispute_bond, vec![CHARLIE])), + ) + .unwrap(), + outcome_info ); } From cd6f57542420e1ddff50821b249b39e98d73e30b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 10 Feb 2023 15:43:27 +0100 Subject: [PATCH 076/368] remove push_vote_outcome --- zrml/global-disputes/src/benchmarks.rs | 66 +++--- .../src/global_disputes_pallet_api.rs | 27 +-- zrml/global-disputes/src/lib.rs | 133 ++++++------ zrml/global-disputes/src/tests.rs | 202 ++++++++---------- zrml/global-disputes/src/types.rs | 15 +- zrml/prediction-markets/src/lib.rs | 33 +-- zrml/prediction-markets/src/tests.rs | 6 +- 7 files changed, 238 insertions(+), 244 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index 1eaf726e6..bfca9c80e 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -55,7 +55,7 @@ fn assert_last_event(generic_event: ::Event) { benchmarks! { vote_on_outcome { // only Outcomes owners, but not GlobalDisputesInfo owners is present during vote_on_outcome - let o in 1..T::MaxOwners::get(); + let o in 2..T::MaxOwners::get(); // ensure we have one vote left for the call let v in 0..(T::MaxGlobalDisputeVotes::get() - 1); @@ -72,17 +72,23 @@ benchmarks! { let outcome = OutcomeReport::Scalar(0); let amount: BalanceOf = T::MinOutcomeVoteAmount::get().saturated_into(); deposit::(&caller); + + let mut initial_items: Vec> = Vec::new(); for i in 1..=o { let owner = account("outcomes_owner", i, 0); - GlobalDisputes::::push_vote_outcome( - &market_id, - outcome.clone(), - &owner, - 1_000_000_000u128.saturated_into(), - ) - .unwrap(); + initial_items.push(InitialItem { + outcome: outcome.clone(), + owner, + amount: 1_000_000_000u128.saturated_into(), + }); } + GlobalDisputes::::start_global_dispute( + &market_id, + initial_items.as_slice(), + ) + .unwrap(); + let mut vote_locks: BoundedVec<( MarketIdOf, BalanceOf @@ -328,7 +334,8 @@ benchmarks! { purge_outcomes { // RemoveKeysLimit - 2 to ensure that we actually fully clean and return at the end - let k in 1..(T::RemoveKeysLimit::get() - 2); + // at least two voting outcomes + let k in 2..(T::RemoveKeysLimit::get() - 2); let o in 1..T::MaxOwners::get(); @@ -336,17 +343,22 @@ benchmarks! { let market = market_mock::(); T::MarketCommons::push_market(market).unwrap(); + let mut initial_items: Vec> = Vec::new(); for i in 1..=k { let owner = account("outcomes_owner", i, 0); - GlobalDisputes::::push_vote_outcome( - &market_id, - OutcomeReport::Scalar(i.into()), - &owner, - 1_000_000_000u128.saturated_into(), - ) - .unwrap(); + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(i.into()), + owner, + amount: 1_000_000_000u128.saturated_into(), + }); } + GlobalDisputes::::start_global_dispute( + &market_id, + initial_items.as_slice(), + ) + .unwrap(); + let mut owners = Vec::new(); for i in 1..=o { let owner = account("winners_owner", i, 0); @@ -383,7 +395,8 @@ benchmarks! { refund_vote_fees { // RemoveKeysLimit - 2 to ensure that we actually fully clean and return at the end - let k in 1..(T::RemoveKeysLimit::get() - 2); + // at least two voting outcomes + let k in 2..(T::RemoveKeysLimit::get() - 2); let o in 1..T::MaxOwners::get(); @@ -391,17 +404,22 @@ benchmarks! { let market = market_mock::(); T::MarketCommons::push_market(market).unwrap(); + let mut initial_items: Vec> = Vec::new(); for i in 1..=k { let owner = account("outcomes_owner", i, 0); - GlobalDisputes::::push_vote_outcome( - &market_id, - OutcomeReport::Scalar(i.into()), - &owner, - 1_000_000_000u128.saturated_into(), - ) - .unwrap(); + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(i.into()), + owner, + amount: 1_000_000_000u128.saturated_into(), + }); } + GlobalDisputes::::start_global_dispute( + &market_id, + initial_items.as_slice(), + ) + .unwrap(); + let mut owners = Vec::new(); for i in 1..=o { let owner = account("winners_owner", i, 0); diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index df4218ffb..80fde2c22 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -17,7 +17,8 @@ extern crate alloc; -use sp_runtime::{DispatchError, DispatchResult}; +use crate::types::InitialItem; +use sp_runtime::DispatchError; use zeitgeist_primitives::types::OutcomeReport; /// The trait to initiate and resolve the global disputes. @@ -28,19 +29,15 @@ pub trait GlobalDisputesPalletApi { /// Return the `GdVotingPeriod` parameter. fn get_vote_period() -> BlockNumber; - /// Push a voting outcome for one global dispute. + /// Start a global dispute. /// /// # Arguments /// - `market_id` - The id of the market. - /// - `outcome` - The voting outcome to push. - /// - `owner` - The owner of the outcome. - /// - `initial_vote_balance` - The initial vote amount for the specified outcome. - fn push_vote_outcome( + /// - `initial_items` - The initial items (outcome, owner, amount) to add to the global dispute. + fn start_global_dispute( market_id: &MarketId, - outcome: OutcomeReport, - owner: &AccountId, - initial_vote_balance: Balance, - ) -> DispatchResult; + initial_items: &[InitialItem], + ) -> Result; /// Determine the winner of a global dispute. /// @@ -55,18 +52,12 @@ pub trait GlobalDisputesPalletApi { /// Check if a global dispute exists for the specified market. fn does_exist(market_id: &MarketId) -> bool; - /// Check if global dispute is active or initialized. But not finished. + /// Check if global dispute is active. /// This call is useful to check if a global dispute is ready for a destruction. /// /// # Arguments /// - `market_id` - The id of the market. - fn is_unfinished(market_id: &MarketId) -> bool; - - /// Start a global dispute. - /// - /// # Arguments - /// - `market_id` - The id of the market. - fn start_global_dispute(market_id: &MarketId) -> Result; + fn is_active(market_id: &MarketId) -> bool; /// Destroy a global dispute and allow to return all funds of the participants. /// diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 04570e2e0..2a9f274f7 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -31,10 +31,11 @@ pub use global_disputes_pallet_api::GlobalDisputesPalletApi; pub use pallet::*; pub type PossessionOf = crate::types::Possession, BalanceOf, OwnerInfoOf>; +pub type InitialItemOf = crate::types::InitialItem, BalanceOf>; #[frame_support::pallet] mod pallet { - use crate::{types::*, weights::WeightInfoZeitgeist, GlobalDisputesPalletApi}; + use crate::{types::*, weights::WeightInfoZeitgeist, GlobalDisputesPalletApi, InitialItemOf}; use core::marker::PhantomData; use frame_support::{ ensure, log, @@ -229,9 +230,7 @@ mod pallet { /// The amount in the reward pot is zero. NoFundsToReward, /// No global dispute present at the moment. - NoGlobalDisputeInitialized, - /// The global dispute has to be in initialized state during the initial outcome setup. - NotInitialized, + GlobalDisputeNotFound, /// The voting outcome has been already added. OutcomeAlreadyExists, /// The outcome specified is not present in the voting outcomes. @@ -250,6 +249,8 @@ mod pallet { NotInGdVotingPeriod, /// The operation requires a global dispute in a destroyed state. GlobalDisputeNotDestroyed, + /// The global dispute was already started. + GlobalDisputeAlreadyExists, } #[pallet::call] @@ -279,8 +280,8 @@ mod pallet { let market = T::MarketCommons::market(&market_id)?; ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); - let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeInitialized)?; + let gd_info = + >::get(market_id).ok_or(Error::::GlobalDisputeNotFound)?; let now = >::block_number(); if let GdStatus::Active { add_outcome_end, vote_end: _ } = gd_info.status { ensure!(now <= add_outcome_end, Error::::AddOutcomePeriodIsOver); @@ -337,8 +338,8 @@ mod pallet { ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeInitialized)?; + let gd_info = + >::get(market_id).ok_or(Error::::GlobalDisputeNotFound)?; ensure!(gd_info.status == GdStatus::Destroyed, Error::::GlobalDisputeNotDestroyed); let mut owners_len = 0u32; @@ -394,8 +395,8 @@ mod pallet { ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - let mut gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeInitialized)?; + let mut gd_info = + >::get(market_id).ok_or(Error::::GlobalDisputeNotFound)?; ensure!(gd_info.status == GdStatus::Finished, Error::::UnfinishedGlobalDispute); let winning_outcome: Option> = @@ -458,8 +459,8 @@ mod pallet { >::OutcomesNotFullyCleaned ); - let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeInitialized)?; + let gd_info = + >::get(market_id).ok_or(Error::::GlobalDisputeNotFound)?; ensure!(gd_info.status == GdStatus::Finished, Error::::UnfinishedGlobalDispute); let reward_account = Self::reward_account(&market_id); @@ -511,8 +512,8 @@ mod pallet { ensure!(amount <= voter_free_balance, Error::::InsufficientAmount); ensure!(amount >= T::MinOutcomeVoteAmount::get(), Error::::AmountTooLow); - let gd_info = >::get(market_id) - .ok_or(Error::::NoGlobalDisputeInitialized)?; + let gd_info = + >::get(market_id).ok_or(Error::::GlobalDisputeNotFound)?; let now = >::block_number(); if let GdStatus::Active { add_outcome_end, vote_end } = gd_info.status { ensure!(add_outcome_end < now && now <= vote_end, Error::::NotInGdVotingPeriod); @@ -679,6 +680,7 @@ mod pallet { market_id, |highest: &mut Option>| { *highest = Some(highest.clone().map_or( + // if never a highest was present set the first here GlobalDisputeInfo::new(outcome.clone(), outcome_info.possession, amount), |mut prev_gd_info| { if amount >= prev_gd_info.outcome_info.outcome_sum { @@ -766,49 +768,6 @@ mod pallet { T::GdVotingPeriod::get() } - fn push_vote_outcome( - market_id: &MarketIdOf, - outcome: OutcomeReport, - owner: &T::AccountId, - initial_vote_balance: BalanceOf, - ) -> DispatchResult { - let market = T::MarketCommons::market(market_id)?; - ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); - - if let Some(gd_info) = >::get(market_id) { - ensure!(gd_info.status == GdStatus::Initialized, Error::::NotInitialized); - } - - match >::get(market_id, &outcome) { - Some(mut outcome_info) => { - let outcome_sum = outcome_info.outcome_sum.saturating_add(initial_vote_balance); - outcome_info.outcome_sum = outcome_sum; - let mut owners = outcome_info - .possession - .get_shared_owners() - .ok_or(Error::::SharedPossessionRequired)?; - owners.try_push(owner.clone()).map_err(|_| Error::::MaxOwnersReached)?; - outcome_info.possession = Possession::Shared { owners }; - Self::update_winner(market_id, &outcome, outcome_info.clone()); - >::insert(market_id, outcome, outcome_info); - } - None => { - // adding one item to BoundedVec can not fail - if let Ok(owners) = BoundedVec::try_from(vec![owner.clone()]) { - let possession = Possession::Shared { owners }; - let outcome_info = - OutcomeInfo { outcome_sum: initial_vote_balance, possession }; - Self::update_winner(market_id, &outcome, outcome_info.clone()); - >::insert(market_id, outcome, outcome_info); - } else { - log::error!("Global Disputes: Could not construct a bounded vector."); - debug_assert!(false); - } - } - } - Ok(()) - } - fn determine_voting_winner(market_id: &MarketIdOf) -> Option { match >::get(market_id) { Some(mut gd_info) => { @@ -828,33 +787,66 @@ mod pallet { >::get(market_id).is_some() } - fn is_unfinished(market_id: &MarketIdOf) -> bool { + fn is_active(market_id: &MarketIdOf) -> bool { if let Some(gd_info) = >::get(market_id) { - return matches!( - gd_info.status, - GdStatus::Active { add_outcome_end: _, vote_end: _ } | GdStatus::Initialized - ); + if let GdStatus::Active { add_outcome_end: _, vote_end: _ } = gd_info.status { + return true; + } } false } - fn start_global_dispute(market_id: &MarketIdOf) -> Result { - T::MarketCommons::market(market_id)?; + fn start_global_dispute( + market_id: &MarketIdOf, + initial_items: &[InitialItemOf], + ) -> Result { + let market = T::MarketCommons::market(market_id)?; - let mut iter = >::iter_prefix(market_id); ensure!( - iter.next().is_some() && iter.next().is_some(), - Error::::AtLeastTwoOutcomesRequired + >::get(market_id).is_none(), + Error::::GlobalDisputeAlreadyExists ); + ensure!(initial_items.len() >= 2, Error::::AtLeastTwoOutcomesRequired); + + for InitialItem { outcome, owner, amount } in initial_items { + ensure!(market.matches_outcome_report(outcome), Error::::OutcomeMismatch); + + match >::get(market_id, outcome) { + Some(mut outcome_info) => { + let outcome_sum = outcome_info.outcome_sum.saturating_add(*amount); + outcome_info.outcome_sum = outcome_sum; + let mut owners = outcome_info + .possession + .get_shared_owners() + .ok_or(Error::::SharedPossessionRequired)?; + owners.try_push(owner.clone()).map_err(|_| Error::::MaxOwnersReached)?; + outcome_info.possession = Possession::Shared { owners }; + Self::update_winner(market_id, outcome, outcome_info.clone()); + >::insert(market_id, outcome, outcome_info); + } + None => { + // adding one item to BoundedVec can not fail + if let Ok(owners) = BoundedVec::try_from(vec![owner.clone()]) { + let possession = Possession::Shared { owners }; + let outcome_info = OutcomeInfo { outcome_sum: *amount, possession }; + Self::update_winner(market_id, outcome, outcome_info.clone()); + >::insert(market_id, outcome, outcome_info); + } else { + log::error!("Global Disputes: Could not construct a bounded vector."); + debug_assert!(false); + } + } + } + } + let now = >::block_number(); let add_outcome_end = now.saturating_add(T::AddOutcomePeriod::get()); let vote_end = add_outcome_end.saturating_add(T::GdVotingPeriod::get()); let ids_len = T::DisputeResolution::add_auto_resolve(market_id, vote_end)?; >::try_mutate(market_id, |gd_info| -> DispatchResult { - let mut raw_gd_info = - gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; + let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::GlobalDisputeNotFound)?; raw_gd_info.status = GdStatus::Active { add_outcome_end, vote_end }; *gd_info = Some(raw_gd_info.clone()); Ok(()) @@ -865,8 +857,7 @@ mod pallet { fn destroy_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { >::try_mutate(market_id, |gd_info| { - let mut raw_gd_info = - gd_info.as_mut().ok_or(Error::::NoGlobalDisputeInitialized)?; + let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::GlobalDisputeNotFound)?; raw_gd_info.status = GdStatus::Destroyed; if let GdStatus::Active { add_outcome_end: _, vote_end } = raw_gd_info.status { T::DisputeResolution::remove_auto_resolve(market_id, vote_end); diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index d3d0fb5c7..fef483dcd 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -20,9 +20,9 @@ use crate::{ global_disputes_pallet_api::GlobalDisputesPalletApi, mock::*, - types::{GdStatus, GlobalDisputeInfo, OutcomeInfo, Possession}, + types::{GdStatus, GlobalDisputeInfo, InitialItem, OutcomeInfo, Possession}, utils::market_mock, - BalanceOf, Error, Event, GlobalDisputesInfo, Locks, MarketIdOf, Outcomes, + BalanceOf, Error, Event, GlobalDisputesInfo, InitialItemOf, Locks, MarketIdOf, Outcomes, }; use frame_support::{ assert_noop, assert_ok, @@ -45,17 +45,13 @@ fn the_lock(amount: u128) -> BalanceLock { BalanceLock { id: GlobalDisputeLockId::get(), amount, reasons: pallet_balances::Reasons::Misc } } -fn setup_vote_outcomes_with_hundred(market_id: &MarketIdOf) { - GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(0), &ALICE, SETUP_AMOUNT) - .unwrap(); - - GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(20), &ALICE, SETUP_AMOUNT) - .unwrap(); - GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(40), &ALICE, SETUP_AMOUNT) - .unwrap(); - - GlobalDisputes::push_vote_outcome(market_id, OutcomeReport::Scalar(60), &ALICE, SETUP_AMOUNT) - .unwrap(); +fn get_initial_items() -> Vec> { + vec![ + InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(20), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(40), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(60), owner: ALICE, amount: SETUP_AMOUNT }, + ] } fn set_vote_period() { @@ -86,8 +82,8 @@ fn add_vote_outcome_works() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); let free_balance_alice_before = Balances::free_balance(&ALICE); let free_balance_reward_account = @@ -124,8 +120,8 @@ fn add_vote_outcome_fails_with_outcome_mismatch() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); assert_noop!( GlobalDisputes::add_vote_outcome( @@ -165,7 +161,7 @@ fn add_vote_outcome_fails_if_no_global_dispute_present() { market_id, OutcomeReport::Scalar(20), ), - Error::::NoGlobalDisputeInitialized + Error::::GlobalDisputeNotFound ); }); } @@ -199,9 +195,8 @@ fn add_vote_outcome_fails_if_outcome_already_exists() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); - + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); >::insert( market_id, OutcomeReport::Scalar(20), @@ -228,8 +223,8 @@ fn add_vote_outcome_fails_if_balance_too_low() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); assert_noop!( GlobalDisputes::add_vote_outcome( @@ -393,8 +388,8 @@ fn vote_fails_if_amount_below_min_outcome_vote_amount() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); assert_noop!( GlobalDisputes::vote_on_outcome( @@ -415,8 +410,8 @@ fn vote_fails_for_insufficient_funds() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); // Paul does not have 50 * BASE assert_noop!( @@ -438,8 +433,8 @@ fn determine_voting_winner_sets_the_last_outcome_for_same_vote_balances_as_the_c let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -471,8 +466,6 @@ fn determine_voting_winner_sets_the_last_outcome_for_same_vote_balances_as_the_c 42 * BASE )); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); - assert_eq!( &GlobalDisputes::determine_voting_winner(&market_id).unwrap(), &OutcomeReport::Scalar(60) @@ -490,8 +483,8 @@ fn vote_on_outcome_check_event() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -532,23 +525,20 @@ fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { free_balance_disputor_before - reserved_balance_disputor ); - GlobalDisputes::push_vote_outcome( - &market_id, - OutcomeReport::Scalar(0), - &ALICE, - reserved_balance_disputor, - ) - .unwrap(); - - GlobalDisputes::push_vote_outcome( - &market_id, - OutcomeReport::Scalar(20), - &ALICE, - reserved_balance_disputor * 2, - ) - .unwrap(); + let initial_items = vec![ + InitialItem { + outcome: OutcomeReport::Scalar(0), + owner: ALICE, + amount: reserved_balance_disputor, + }, + InitialItem { + outcome: OutcomeReport::Scalar(20), + owner: ALICE, + amount: reserved_balance_disputor * 2, + }, + ]; - GlobalDisputes::start_global_dispute(&market_id).unwrap(); + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()).unwrap(); set_vote_period(); @@ -585,9 +575,8 @@ fn transfer_fails_with_fully_locked_balance() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -623,8 +612,8 @@ fn reserve_fails_with_fully_locked_balance() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -660,8 +649,8 @@ fn determine_voting_winner_works_four_outcome_votes() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -715,8 +704,8 @@ fn determine_voting_winner_works_three_outcome_votes() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -764,8 +753,8 @@ fn determine_voting_winner_works_two_outcome_votes() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -813,8 +802,8 @@ fn determine_voting_winner_works_with_accumulated_votes_for_alice() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -876,8 +865,8 @@ fn purge_outcomes_fully_cleaned_works() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -919,17 +908,16 @@ fn purge_outcomes_partially_cleaned_works() { let market = market_mock::(); Markets::::insert(market_id, &market); + let mut initial_items = Vec::new(); for i in 0..(2 * RemoveKeysLimit::get()) { - GlobalDisputes::push_vote_outcome( - &market_id, - OutcomeReport::Scalar(i.into()), - &ALICE, - SETUP_AMOUNT, - ) - .unwrap(); + initial_items.push(InitialItem { + owner: ALICE, + outcome: OutcomeReport::Scalar(i.into()), + amount: SETUP_AMOUNT, + }); } - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); assert!(GlobalDisputes::determine_voting_winner(&market_id).is_some()); @@ -955,26 +943,24 @@ fn refund_vote_fees_works() { Markets::::insert(market_id, &market); let pushed_outcome_1 = 0; - GlobalDisputes::push_vote_outcome( - &market_id, - OutcomeReport::Scalar(pushed_outcome_1), - &ALICE, - SETUP_AMOUNT, - ) - .unwrap(); - let pushed_outcome_2 = 20; - GlobalDisputes::push_vote_outcome( - &market_id, - OutcomeReport::Scalar(pushed_outcome_2), - &ALICE, - SETUP_AMOUNT, - ) - .unwrap(); + + let initial_items = vec![ + InitialItem { + owner: ALICE, + outcome: OutcomeReport::Scalar(pushed_outcome_1), + amount: SETUP_AMOUNT, + }, + InitialItem { + owner: ALICE, + outcome: OutcomeReport::Scalar(pushed_outcome_2), + amount: SETUP_AMOUNT, + }, + ]; let offset = pushed_outcome_1.max(pushed_outcome_2) + 1; - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); let mut overall_fees = >::zero(); // minus 2 because of the above push_vote_outcome calls @@ -1017,8 +1003,8 @@ fn unlock_clears_lock_info() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -1046,16 +1032,14 @@ fn vote_fails_if_outcome_does_not_exist() { let market = market_mock::(); Markets::::insert(market_id, &market); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(0), &ALICE, 10 * BASE) - .unwrap(); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(20), &ALICE, 20 * BASE) - .unwrap(); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(40), &ALICE, 30 * BASE) - .unwrap(); - GlobalDisputes::push_vote_outcome(&market_id, OutcomeReport::Scalar(60), &ALICE, 40 * BASE) - .unwrap(); + let initial_items = vec![ + InitialItem { owner: ALICE, outcome: OutcomeReport::Scalar(0), amount: 10 * BASE }, + InitialItem { owner: ALICE, outcome: OutcomeReport::Scalar(20), amount: 20 * BASE }, + InitialItem { owner: ALICE, outcome: OutcomeReport::Scalar(40), amount: 30 * BASE }, + InitialItem { owner: ALICE, outcome: OutcomeReport::Scalar(60), amount: 40 * BASE }, + ]; - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -1078,8 +1062,8 @@ fn locking_works_for_one_market() { let market = market_mock::(); Markets::::insert(market_id, &market); - setup_vote_outcomes_with_hundred(&market_id); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); set_vote_period(); @@ -1173,10 +1157,10 @@ fn locking_works_for_two_markets_with_stronger_first_unlock() { let market_2 = market_mock::(); Markets::::insert(market_id_2, &market_2); - setup_vote_outcomes_with_hundred(&market_id_1); - setup_vote_outcomes_with_hundred(&market_id_2); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1)); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1, initial_items.as_slice())); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2, initial_items.as_slice())); set_vote_period(); @@ -1272,10 +1256,10 @@ fn locking_works_for_two_markets_with_weaker_first_unlock() { let market_2 = market_mock::(); Markets::::insert(market_id_2, &market_2); - setup_vote_outcomes_with_hundred(&market_id_1); - setup_vote_outcomes_with_hundred(&market_id_2); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1)); - assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2)); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1, initial_items.as_slice())); + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id_2, initial_items.as_slice())); set_vote_period(); diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 20445a5bb..ac2d2d544 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -59,7 +59,7 @@ pub struct GlobalDisputeInfo { pub status: GdStatus, } -impl +impl GlobalDisputeInfo { pub fn new( @@ -68,7 +68,10 @@ impl vote_sum: Balance, ) -> Self { let outcome_info = OutcomeInfo { outcome_sum: vote_sum, possession }; - GlobalDisputeInfo { winner_outcome: outcome, status: GdStatus::Initialized, outcome_info } + // `add_outcome_end` and `vote_end` gets set in `start_global_dispute` + let status = + GdStatus::Active { add_outcome_end: Default::default(), vote_end: Default::default() }; + GlobalDisputeInfo { winner_outcome: outcome, status, outcome_info } } pub fn update_winner(&mut self, outcome: OutcomeReport, vote_sum: Balance) { @@ -80,8 +83,6 @@ impl /// The current status of the global dispute. #[derive(TypeInfo, Debug, Decode, Encode, MaxEncodedLen, Clone, PartialEq, Eq)] pub enum GdStatus { - /// The global dispute is initialized. - Initialized, /// The global dispute is in progress. /// The block number `add_outcome_end`, when the addition of new outcomes is over. /// The block number `vote_end`, when the global dispute voting period is over. @@ -120,3 +121,9 @@ impl OldWinnerInfo OldWinnerInfo { outcome, is_finished: false, outcome_info } } } + +pub struct InitialItem { + pub outcome: OutcomeReport, + pub owner: AccountId, + pub amount: Balance, +} diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 435095d06..ab53b927a 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -70,7 +70,7 @@ mod pallet { }, }; #[cfg(feature = "with-global-disputes")] - use zrml_global_disputes::GlobalDisputesPalletApi; + use zrml_global_disputes::{types::InitialItem, GlobalDisputesPalletApi}; use zrml_liquidity_mining::LiquidityMiningPalletApi; use zrml_market_commons::MarketCommonsPalletApi; @@ -98,6 +98,8 @@ mod pallet { pub type CacheSize = ConstU32<64>; pub type EditReason = BoundedVec::MaxEditReasonLen>; pub type RejectReason = BoundedVec::MaxRejectReasonLen>; + #[cfg(feature = "with-global-disputes")] + type InitialItemOf = InitialItem<::AccountId, BalanceOf>; macro_rules! impl_unreserve_bond { ($fn_name:ident, $bond_type:ident) => { @@ -344,7 +346,7 @@ mod pallet { } #[cfg(feature = "with-global-disputes")] - if T::GlobalDisputes::is_unfinished(&market_id) { + if T::GlobalDisputes::is_active(&market_id) { T::GlobalDisputes::destroy_global_dispute(&market_id)?; } @@ -1472,24 +1474,24 @@ mod pallet { Error::::GlobalDisputeExistsAlready ); + let mut initial_items: Vec> = Vec::new(); + // add report outcome to voting choices if let Some(report) = &market.report { - T::GlobalDisputes::push_vote_outcome( - &market_id, - report.outcome.clone(), - &report.by, - >::zero(), - )?; + initial_items.push(InitialItemOf:: { + outcome: report.outcome.clone(), + owner: report.by.clone(), + amount: >::zero(), + }); } for (index, MarketDispute { at: _, by, outcome }) in disputes.iter().enumerate() { let dispute_bond = default_dispute_bond::(index); - T::GlobalDisputes::push_vote_outcome( - &market_id, - outcome.clone(), - by, - dispute_bond, - )?; + initial_items.push(InitialItemOf:: { + outcome: outcome.clone(), + owner: by.clone(), + amount: dispute_bond, + }); } // ensure, that global disputes controls the resolution now @@ -1499,7 +1501,8 @@ mod pallet { let (_, ids_len_2) = Self::clear_auto_resolve(&market_id)?; // global disputes uses DisputeResolution API to control its resolution - let ids_len_1 = T::GlobalDisputes::start_global_dispute(&market_id)?; + let ids_len_1 = + T::GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())?; Self::deposit_event(Event::GlobalDisputeStarted(market_id)); diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 755530070..4d66a69f4 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3042,7 +3042,7 @@ fn start_global_dispute_works() { }; let outcome_info = OutcomeInfo { outcome_sum: Zero::zero(), possession }; assert_eq!( - Outcomes::::get(&market_id, &OutcomeReport::Categorical(0)).unwrap(), + Outcomes::::get(market_id, &OutcomeReport::Categorical(0)).unwrap(), outcome_info ); let possession: PossessionOf = Possession::Shared { @@ -3054,7 +3054,7 @@ fn start_global_dispute_works() { OutcomeInfo { outcome_sum: dispute_bond, possession: possession.clone() }; assert_eq!( Outcomes::::get( - &market_id, + market_id, &OutcomeReport::Categorical(i.saturated_into()) ) .unwrap(), @@ -3070,7 +3070,7 @@ fn start_global_dispute_works() { let vote_end = add_outcome_end + GlobalDisputes::get_vote_period(); let market_ids = MarketIdsPerDisputeBlock::::get(vote_end); assert_eq!(market_ids, vec![market_id]); - assert!(GlobalDisputes::is_unfinished(&market_id)); + assert!(GlobalDisputes::is_active(&market_id)); System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); assert_noop!( From 0973455df2e76a0259ec7681b93c32afbfeef482 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 13 Feb 2023 15:41:09 +0100 Subject: [PATCH 077/368] apply review suggestion --- zrml/global-disputes/src/tests.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index fef483dcd..ed12a7267 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -31,11 +31,12 @@ use frame_support::{ }; use pallet_balances::{BalanceLock, Error as BalancesError}; use sp_runtime::traits::Zero; +use test_case::test_case; use zeitgeist_primitives::{ constants::mock::{ GlobalDisputeLockId, MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, BASE, }, - types::OutcomeReport, + types::{BlockNumber, OutcomeReport}, }; use zrml_market_commons::{Error as MarketError, Markets}; @@ -166,15 +167,16 @@ fn add_vote_outcome_fails_if_no_global_dispute_present() { }); } -#[test] -fn add_vote_outcome_fails_if_global_dispute_finished() { +#[test_case(GdStatus::Finished; "finished")] +#[test_case(GdStatus::Destroyed; "destroyed")] +fn add_vote_outcome_fails_if_global_dispute_is_in_wrong_state(status: GdStatus) { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); Markets::::insert(market_id, &market); let possession = Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap() }; let mut gd_info = GlobalDisputeInfo::new(OutcomeReport::Scalar(0), possession, 10 * BASE); - gd_info.status = GdStatus::Finished; + gd_info.status = status; >::insert(market_id, gd_info); assert_noop!( From 358d23980babf53f84da40d6bad66a75001c1457 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 14 Feb 2023 09:53:09 +0100 Subject: [PATCH 078/368] update changelog for devs --- docs/changelog_for_devs.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index 315e0230b..65a3b7c7c 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -1,8 +1,28 @@ # v0.3.9 +All things about Global Disputes: - Added new dispatchable function: - `refund_vote_fees` - Return all vote funds and fees, when a global dispute was destroyed. +- Added the following events: + - `OutcomeOwnerRewarded` for `Possession::Paid` + - `OutcomeOwnersRewarded` for `Possession::Shared` + - `OutcomesFullyCleaned` and `OutcomesPartiallyCleaned` for extrinsic `refund_vote_fees` +- Removed the following events: +- Added enum `Possession` with variants: + - `Paid { owner: AccountId, fee: Balance }` + - `Shared { owners: BoundedVec }` +- `OutcomeInfo` has the following fields: + - `outcome_sum: Balance` + - `possession: Possession` +- Replace `WinnerInfo` by `GlobalDisputeInfo` with the following fields: + - `winner_outcome: OutcomeReport` + - `outcome_info: OutcomeInfo` + - `status: GdStatus` +- Added `GdStatus` with the following enum variants: + - `Active { add_outcome_end: BlockNumber, vote_end: BlockNumber }` + - `Finished` + - `Destroyed` # v0.3.8 From de7d61ea9153d04261000c7edb2675650c5a1843 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 14 Feb 2023 11:35:18 +0100 Subject: [PATCH 079/368] add tests for start_global_dispute --- zrml/global-disputes/src/tests.rs | 175 ++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index ed12a7267..ff7f2c37f 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -113,6 +113,181 @@ fn add_vote_outcome_works() { }); } +#[test_case(GdStatus::Finished; "finished")] +#[test_case(GdStatus::Destroyed; "destroyed")] +fn is_active_works(status: GdStatus) { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + assert!(!GlobalDisputes::is_active(&market_id)); + + let outcome_info = OutcomeInfo { + outcome_sum: 0, + possession: Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap() }, + }; + >::insert( + market_id, + GlobalDisputeInfo { + winner_outcome: OutcomeReport::Scalar(0), + outcome_info: outcome_info.clone(), + status, + }, + ); + + assert!(!GlobalDisputes::is_active(&market_id)); + + >::insert( + market_id, + GlobalDisputeInfo { + winner_outcome: OutcomeReport::Scalar(0), + outcome_info, + status: GdStatus::Active { add_outcome_end: 0, vote_end: 0 }, + }, + ); + + assert!(GlobalDisputes::is_active(&market_id)); + }); +} + +#[test] +fn destroy_global_dispute_works() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let initial_items = get_initial_items(); + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); + + assert_ok!(GlobalDisputes::destroy_global_dispute(&market_id)); + + assert_eq!( + >::get(&market_id).unwrap().status, + GdStatus::Destroyed + ); + }); +} + +#[test] +fn start_global_dispute_works() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let initial_items = vec![ + InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(20), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(40), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(60), owner: ALICE, amount: SETUP_AMOUNT }, + ]; + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); + + let outcome_info = OutcomeInfo { + outcome_sum: SETUP_AMOUNT, + possession: Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap() }, + }; + assert_eq!( + >::get(&market_id).unwrap(), + GlobalDisputeInfo { + winner_outcome: OutcomeReport::Scalar(60), + outcome_info, + status: GdStatus::Active { add_outcome_end: 21, vote_end: 161 }, + } + ); + }); +} + +#[test] +fn start_global_dispute_fails_if_outcome_mismatch() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let initial_items = vec![ + InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(20), owner: ALICE, amount: SETUP_AMOUNT }, + // categorical outcome mismatch + InitialItem { + outcome: OutcomeReport::Categorical(40), + owner: ALICE, + amount: SETUP_AMOUNT, + }, + InitialItem { outcome: OutcomeReport::Scalar(60), owner: ALICE, amount: SETUP_AMOUNT }, + ]; + assert_eq!( + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), + Err(Error::::OutcomeMismatch.into()) + ); + }); +} + +#[test] +fn start_global_dispute_fails_if_less_than_two_outcomes() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let initial_items = vec![InitialItem { + outcome: OutcomeReport::Scalar(0), + owner: ALICE, + amount: SETUP_AMOUNT, + }]; + assert_eq!( + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), + Err(Error::::AtLeastTwoOutcomesRequired.into()) + ); + }); +} + +#[test] +fn start_global_dispute_fails_if_already_exists() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let initial_items = vec![ + InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(20), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(40), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(60), owner: ALICE, amount: SETUP_AMOUNT }, + ]; + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()).unwrap(); + assert_eq!( + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), + Err(Error::::GlobalDisputeAlreadyExists.into()) + ); + }); +} + +#[test] +fn start_global_dispute_fails_if_max_owner_reached() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let mut initial_items = Vec::new(); + for i in 0..MaxOwners::get() + 1 { + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(0), + owner: i.into(), + amount: SETUP_AMOUNT, + }); + } + + assert_eq!( + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), + Err(Error::::MaxOwnersReached.into()) + ); + }); +} + #[test] fn add_vote_outcome_fails_with_outcome_mismatch() { ExtBuilder::default().build().execute_with(|| { From 85b248043ae16574eead49a164b725d7a77ed539 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 14 Feb 2023 12:08:14 +0100 Subject: [PATCH 080/368] correct benchmarks --- zrml/global-disputes/src/benchmarks.rs | 46 +++++++++++++++++++++++++- zrml/global-disputes/src/lib.rs | 4 +-- zrml/global-disputes/src/weights.rs | 19 ++++++++--- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index bfca9c80e..4bf5e1ea5 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -274,7 +274,7 @@ benchmarks! { ); } - reward_outcome_owner_with_funds { + reward_outcome_owner_shared_possession { let o in 1..T::MaxOwners::get(); let market_id: MarketIdOf = 0u128.saturated_into(); @@ -332,6 +332,50 @@ benchmarks! { assert_eq!(T::Currency::free_balance(&reward_account), expected); } + reward_outcome_owner_paid_possession { + let market_id: MarketIdOf = 0u128.saturated_into(); + + let owner: AccountIdOf = account("winners_owner", 0, 0); + let possession = Possession::Paid { owner: owner.clone(), fee: T::VotingOutcomeFee::get() }; + let gd_info = GlobalDisputeInfo { + winner_outcome: OutcomeReport::Scalar(0), + status: GdStatus::Finished, + outcome_info: OutcomeInfo { + outcome_sum: 42u128.saturated_into(), + possession, + }, + }; + >::insert(market_id, gd_info.clone()); + + let reward_account = GlobalDisputes::::reward_account(&market_id); + let _ = T::Currency::deposit_creating( + &reward_account, + T::VotingOutcomeFee::get().saturating_mul(10u128.saturated_into()), + ); + let reward_before = T::Currency::free_balance(&reward_account); + + let caller: T::AccountId = whitelisted_caller(); + + let outcome = OutcomeReport::Scalar(20); + + deposit::(&caller); + }: { + >::reward_outcome_owner( + RawOrigin::Signed(caller.clone()).into(), + market_id + ) + .unwrap(); + } verify { + assert_last_event::( + Event::OutcomeOwnerRewarded:: { + market_id, + owner: owner.clone(), + } + .into(), + ); + assert!(T::Currency::free_balance(&reward_account) == 0u128.saturated_into()); + } + purge_outcomes { // RemoveKeysLimit - 2 to ensure that we actually fully clean and return at the end // at least two voting outcomes diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 2a9f274f7..01c12cbf7 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -733,7 +733,7 @@ mod pallet { market_id, owners: owners.into_inner(), }); - Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(owners_len))).into()) + Ok((Some(T::WeightInfo::reward_outcome_owner_shared_possession(owners_len))).into()) } fn reward_paid_possession( @@ -751,7 +751,7 @@ mod pallet { // not really much we can do if it fails debug_assert!(res.is_ok(), "Global Disputes: Rewarding a outcome owner failed."); Self::deposit_event(Event::OutcomeOwnerRewarded { market_id, owner }); - Ok((Some(T::WeightInfo::reward_outcome_owner_with_funds(1u32))).into()) + Ok((Some(T::WeightInfo::reward_outcome_owner_paid_possession())).into()) } } diff --git a/zrml/global-disputes/src/weights.rs b/zrml/global-disputes/src/weights.rs index 5ca41e590..d953ffeff 100644 --- a/zrml/global-disputes/src/weights.rs +++ b/zrml/global-disputes/src/weights.rs @@ -50,7 +50,8 @@ pub trait WeightInfoZeitgeist { fn unlock_vote_balance_set(l: u32, o: u32) -> Weight; fn unlock_vote_balance_remove(l: u32, o: u32) -> Weight; fn add_vote_outcome(w: u32) -> Weight; - fn reward_outcome_owner_with_funds(o: u32) -> Weight; + fn reward_outcome_owner_shared_possession(o: u32) -> Weight; + fn reward_outcome_owner_paid_possession() -> Weight; fn purge_outcomes(k: u32, o: u32) -> Weight; fn refund_vote_fees(k: u32, o: u32) -> Weight; } @@ -111,15 +112,23 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: GlobalDisputes Outcomes (r:1 w:0) // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:0) // Storage: System Account (r:2 w:2) - fn reward_outcome_owner_with_funds(o: u32) -> Weight { - Weight::from_ref_time(54_189_000) - // Standard Error: 75_000 - .saturating_add(Weight::from_ref_time(29_563_000).saturating_mul(o.into())) + fn reward_outcome_owner_shared_possession(o: u32) -> Weight { + Weight::from_ref_time(36_741_000) + // Standard Error: 20_000 + .saturating_add(Weight::from_ref_time(22_017_000).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(o.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(o.into()))) } + // Storage: GlobalDisputes Outcomes (r:1 w:0) + // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:0) + // Storage: System Account (r:2 w:2) + fn reward_outcome_owner_paid_possession() -> Weight { + Weight::from_ref_time(56_000_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } // Storage: GlobalDisputes GlobalDisputesInfo (r:1 w:1) // Storage: GlobalDisputes Outcomes (r:3 w:2) fn purge_outcomes(k: u32, o: u32) -> Weight { From 5a8411711651141616c89250fe1f3369521461d7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 14 Feb 2023 12:19:34 +0100 Subject: [PATCH 081/368] correct weighting --- zrml/global-disputes/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 01c12cbf7..1b38ac799 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -447,7 +447,8 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of owners for the winning outcome. #[frame_support::transactional] - #[pallet::weight(T::WeightInfo::reward_outcome_owner_with_funds(T::MaxOwners::get()))] + #[pallet::weight(T::WeightInfo::reward_outcome_owner_paid_possession() + .max(T::WeightInfo::reward_outcome_owner_shared_possession(T::MaxOwners::get())))] pub fn reward_outcome_owner( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, From 04afe75b78c3ca474b9d1950694f5644233fa3a0 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 14 Feb 2023 12:31:43 +0100 Subject: [PATCH 082/368] correct clippy --- zrml/global-disputes/src/benchmarks.rs | 2 +- zrml/global-disputes/src/tests.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index 4bf5e1ea5..d0c4b195d 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -345,7 +345,7 @@ benchmarks! { possession, }, }; - >::insert(market_id, gd_info.clone()); + >::insert(market_id, gd_info); let reward_account = GlobalDisputes::::reward_account(&market_id); let _ = T::Currency::deposit_creating( diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index ff7f2c37f..7a077cb46 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -164,7 +164,7 @@ fn destroy_global_dispute_works() { assert_ok!(GlobalDisputes::destroy_global_dispute(&market_id)); assert_eq!( - >::get(&market_id).unwrap().status, + >::get(market_id).unwrap().status, GdStatus::Destroyed ); }); @@ -190,7 +190,7 @@ fn start_global_dispute_works() { possession: Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap() }, }; assert_eq!( - >::get(&market_id).unwrap(), + >::get(market_id).unwrap(), GlobalDisputeInfo { winner_outcome: OutcomeReport::Scalar(60), outcome_info, From e5ad782b3628a9aafdf7b4845c15a6fbcf611978 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 15 Feb 2023 15:22:31 +0100 Subject: [PATCH 083/368] add quick crowdfund interface --- zrml/court/src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b6254ff28..b4184e1f0 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -39,6 +39,53 @@ pub use juror::Juror; pub use juror_status::JurorStatus; pub use pallet::*; +// TODO: remove this crowdfund interface and use the real after crowdfund pallet is merged +use frame_support::pallet_prelude::DispatchError; +use zeitgeist_primitives::types::OutcomeReport; +use frame_support::pallet_prelude::DispatchResult; + +/// The trait for handling of crowdfunds. +pub trait CrowdfundPalletApi { + /// Create a new crowdfund. + /// + /// # Returns + /// - `FundIndex` - The id of the crowdfund. + fn open_crowdfund() -> Result; + + /// Get an iterator over all items of a crowdfund. + /// + /// # Arguments + /// - `fund_index` - The id of the crowdfund. + /// + /// # Returns + /// - `PrefixIterator` - The iterator over all items of the crowdfund. + fn iter_items( + fund_index: u128, + ) -> frame_support::storage::PrefixIterator<(OutcomeReport, Balance)>; + + /// Prepare for all related backers to potentially refund their stake. + /// + /// # Arguments + /// - `fund_index` - The id of the crowdfund. + /// - `item` - The item to refund. + /// - `fee` - The overall fee to charge from the fund item + /// before the backer refunds are possible. + /// + /// # Returns + /// - `NegativeImbalance` - The imbalance that contains the charged fees. + fn prepare_refund( + fund_index: u128, + item: &OutcomeReport, + fee: sp_runtime::Percent, + ) -> Result; + + /// Close a crowdfund. + /// + /// # Arguments + /// - `fund_index` - The id of the crowdfund. + fn close_crowdfund(fund_index: u128) -> DispatchResult; +} + #[frame_support::pallet] mod pallet { use crate::{weights::WeightInfoZeitgeist, CourtPalletApi, Juror, JurorStatus}; @@ -183,6 +230,8 @@ mod pallet { #[pallet::constant] type CourtCaseDuration: Get; + type Crowdfund: crate::CrowdfundPalletApi, NegativeImbalanceOf>; + type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, From e2e23a09a6db747223b5eafd3263ad2f848c66b5 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 16 Feb 2023 10:06:10 +0100 Subject: [PATCH 084/368] use temporary crowdfund noop --- runtime/common/src/lib.rs | 35 ++++++++++++++++++++++++++++ zrml/court/src/mock.rs | 36 +++++++++++++++++++++++++++++ zrml/prediction-markets/src/mock.rs | 36 +++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index f65e3c7fc..ca017a1e0 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -934,7 +934,42 @@ macro_rules! impl_config_traits { type WeightInfo = zrml_authorized::weights::WeightInfo; } + pub struct CrowdfundNoop; + + type CurrencyOf = ::Currency; + type NegativeImbalanceOf = as Currency< + ::AccountId, + >>::NegativeImbalance; + + impl zrml_court::CrowdfundPalletApi> + for CrowdfundNoop + { + fn open_crowdfund() -> Result { + Ok(0) + } + + fn iter_items( + _fund_index: u128, + ) -> frame_support::storage::PrefixIterator<(zeitgeist_primitives::types::OutcomeReport, Balance)> + { + unimplemented!() + } + + fn prepare_refund( + _fund_index: u128, + _item: &zeitgeist_primitives::types::OutcomeReport, + _fee: sp_runtime::Percent, + ) -> Result, frame_support::pallet_prelude::DispatchError> { + Ok(NegativeImbalanceOf::::zero()) + } + + fn close_crowdfund(_fund_index: u128) -> frame_support::pallet_prelude::DispatchResult { + Ok(()) + } + } + impl zrml_court::Config for Runtime { + type Crowdfund = CrowdfundNoop; type CourtCaseDuration = CourtCaseDuration; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 11a94eb72..b8a792602 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -105,7 +105,43 @@ impl DisputeResolutionApi for NoopResolution { } } +pub struct CrowdfundNoop; + +use frame_support::traits::{Currency, Imbalance}; +type CurrencyOf = ::Currency; +type NegativeImbalanceOf = as Currency< + ::AccountId, +>>::NegativeImbalance; + +impl zrml_court::CrowdfundPalletApi> + for CrowdfundNoop +{ + fn open_crowdfund() -> Result { + Ok(0) + } + + fn iter_items( + _fund_index: u128, + ) -> frame_support::storage::PrefixIterator<(zeitgeist_primitives::types::OutcomeReport, Balance)> + { + unimplemented!() + } + + fn prepare_refund( + _fund_index: u128, + _item: &zeitgeist_primitives::types::OutcomeReport, + _fee: sp_runtime::Percent, + ) -> Result, frame_support::pallet_prelude::DispatchError> { + Ok(NegativeImbalanceOf::::zero()) + } + + fn close_crowdfund(_fund_index: u128) -> frame_support::pallet_prelude::DispatchResult { + Ok(()) + } +} + impl crate::Config for Runtime { + type Crowdfund = CrowdfundNoop; type CourtCaseDuration = CourtCaseDuration; type DisputeResolution = NoopResolution; type Event = (); diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 348081f82..d73a90fde 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -275,8 +275,44 @@ impl zrml_authorized::Config for Runtime { type WeightInfo = zrml_authorized::weights::WeightInfo; } +pub struct CrowdfundNoop; + +use frame_support::traits::{Currency, Imbalance}; +type CurrencyOf = ::Currency; +type NegativeImbalanceOf = as Currency< + ::AccountId, +>>::NegativeImbalance; + +impl zrml_court::CrowdfundPalletApi> + for CrowdfundNoop +{ + fn open_crowdfund() -> Result { + Ok(0) + } + + fn iter_items( + _fund_index: u128, + ) -> frame_support::storage::PrefixIterator<(zeitgeist_primitives::types::OutcomeReport, Balance)> + { + unimplemented!() + } + + fn prepare_refund( + _fund_index: u128, + _item: &zeitgeist_primitives::types::OutcomeReport, + _fee: sp_runtime::Percent, + ) -> Result, frame_support::pallet_prelude::DispatchError> { + Ok(NegativeImbalanceOf::::zero()) + } + + fn close_crowdfund(_fund_index: u128) -> frame_support::pallet_prelude::DispatchResult { + Ok(()) + } +} + impl zrml_court::Config for Runtime { type CourtCaseDuration = CourtCaseDuration; + type Crowdfund = CrowdfundNoop; type DisputeResolution = prediction_markets::Pallet; type Event = Event; type MarketCommons = MarketCommons; From e12db83d1081b5f28eb350e3a78b7f4eabed1693 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 16 Feb 2023 13:24:52 +0100 Subject: [PATCH 085/368] wip --- zrml/court/src/juror.rs | 33 ---- zrml/court/src/juror_status.rs | 31 --- zrml/court/src/lib.rs | 340 ++++++++++++++++++++++++--------- zrml/court/src/types.rs | 143 ++++++++++++++ 4 files changed, 398 insertions(+), 149 deletions(-) delete mode 100644 zrml/court/src/juror.rs delete mode 100644 zrml/court/src/juror_status.rs create mode 100644 zrml/court/src/types.rs diff --git a/zrml/court/src/juror.rs b/zrml/court/src/juror.rs deleted file mode 100644 index 699dc1443..000000000 --- a/zrml/court/src/juror.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021-2022 Zeitgeist PM LLC. -// -// This file is part of Zeitgeist. -// -// Zeitgeist is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 3 of the License, or (at -// your option) any later version. -// -// Zeitgeist is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Zeitgeist. If not, see . - -use crate::JurorStatus; - -// Structure currently has only one field but acts as a container for possible future additions. -#[derive( - parity_scale_codec::Decode, - parity_scale_codec::Encode, - parity_scale_codec::MaxEncodedLen, - scale_info::TypeInfo, - Clone, - Debug, - PartialEq, - Eq, -)] -pub struct Juror { - pub(crate) status: JurorStatus, -} diff --git a/zrml/court/src/juror_status.rs b/zrml/court/src/juror_status.rs deleted file mode 100644 index 6b59fdb42..000000000 --- a/zrml/court/src/juror_status.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021-2022 Zeitgeist PM LLC. -// -// This file is part of Zeitgeist. -// -// Zeitgeist is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 3 of the License, or (at -// your option) any later version. -// -// Zeitgeist is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Zeitgeist. If not, see . - -#[derive( - parity_scale_codec::Decode, - parity_scale_codec::Encode, - parity_scale_codec::MaxEncodedLen, - scale_info::TypeInfo, - Clone, - Debug, - PartialEq, - Eq, -)] -pub enum JurorStatus { - Ok, - Tardy, -} diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b4184e1f0..7717e866f 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -27,50 +27,57 @@ extern crate alloc; mod benchmarks; mod court_pallet_api; -mod juror; -mod juror_status; pub mod migrations; mod mock; mod tests; +mod types; pub mod weights; pub use court_pallet_api::CourtPalletApi; -pub use juror::Juror; -pub use juror_status::JurorStatus; pub use pallet::*; +pub use types::{CourtInfo, CrowdfundInfo, Juror, JurorStatus, Periods, Vote}; // TODO: remove this crowdfund interface and use the real after crowdfund pallet is merged -use frame_support::pallet_prelude::DispatchError; +use frame_support::pallet_prelude::{DispatchError, DispatchResult}; use zeitgeist_primitives::types::OutcomeReport; -use frame_support::pallet_prelude::DispatchResult; /// The trait for handling of crowdfunds. pub trait CrowdfundPalletApi { /// Create a new crowdfund. - /// + /// /// # Returns /// - `FundIndex` - The id of the crowdfund. fn open_crowdfund() -> Result; /// Get an iterator over all items of a crowdfund. - /// + /// /// # Arguments /// - `fund_index` - The id of the crowdfund. - /// + /// /// # Returns /// - `PrefixIterator` - The iterator over all items of the crowdfund. fn iter_items( fund_index: u128, ) -> frame_support::storage::PrefixIterator<(OutcomeReport, Balance)>; + /// Maybe get an item of a crowdfund. + /// + /// # Arguments + /// - `fund_index` - The id of the crowdfund. + /// - `item` - The item to get. + /// + /// # Returns + /// - `Option` - The balance of the item. + fn get_item(fund_index: u128, item: &OutcomeReport) -> Option; + /// Prepare for all related backers to potentially refund their stake. - /// + /// /// # Arguments /// - `fund_index` - The id of the crowdfund. /// - `item` - The item to refund. /// - `fee` - The overall fee to charge from the fund item /// before the backer refunds are possible. - /// + /// /// # Returns /// - `NegativeImbalance` - The imbalance that contains the charged fees. fn prepare_refund( @@ -88,7 +95,10 @@ pub trait CrowdfundPalletApi { #[frame_support::pallet] mod pallet { - use crate::{weights::WeightInfoZeitgeist, CourtPalletApi, Juror, JurorStatus}; + use crate::{ + weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CrowdfundInfo, CrowdfundPalletApi, + Juror, JurorStatus, Periods, Vote, + }; use alloc::{ collections::{BTreeMap, BTreeSet}, vec::Vec, @@ -98,24 +108,24 @@ mod pallet { use frame_support::{ dispatch::DispatchResult, ensure, - pallet_prelude::{CountedStorageMap, StorageDoubleMap, StorageValue, ValueQuery}, + pallet_prelude::{ + CountedStorageMap, OptionQuery, StorageDoubleMap, StorageMap, StorageValue, ValueQuery, + }, traits::{ BalanceStatus, Currency, Get, Hooks, IsType, NamedReservableCurrency, Randomness, StorageVersion, }, - Blake2_128Concat, PalletId, + transactional, Blake2_128Concat, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng}; use sp_runtime::{ - traits::{AccountIdConversion, CheckedDiv, Saturating}, + traits::{AccountIdConversion, CheckedDiv, Hash, Saturating}, ArithmeticError, DispatchError, SaturatedConversion, }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, - types::{ - Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport, - }, + types::{Asset, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport}, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -147,38 +157,29 @@ mod pallet { MomentOf, Asset>, >; + pub(crate) type CourtOf = CourtInfo, ::BlockNumber>; #[pallet::call] impl Pallet { - #[pallet::weight(1_000_000_000_000)] - pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { - // TODO take a bond from the caller - ensure_signed(origin)?; - let market = T::MarketCommons::market(&market_id)?; - ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Court, - Error::::MarketDoesNotHaveCourtMechanism - ); - - let jurors: Vec<_> = Jurors::::iter().collect(); - // TODO &[] was disputes list before: how to handle it now without disputes from pm? - let necessary_jurors_num = Self::necessary_jurors_num(&[]); - let mut rng = Self::rng(); - let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); - let curr_block_num = >::block_number(); - let block_limit = curr_block_num.saturating_add(T::CourtCaseDuration::get()); - for (ai, _) in random_jurors { - RequestedJurors::::insert(market_id, ai, block_limit); + #[pallet::weight(T::WeightInfo::join_court())] + #[transactional] + pub fn join_court(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + if Jurors::::get(&who).is_some() { + return Err(Error::::JurorAlreadyExists.into()); } - - Self::deposit_event(Event::MarketAppealed { market_id }); - + let jurors_num = Jurors::::count() as usize; + let jurors_num_plus_one = jurors_num.checked_add(1).ok_or(ArithmeticError::Overflow)?; + let stake = Self::current_required_stake(jurors_num_plus_one); + CurrencyOf::::reserve_named(&Self::reserve_id(), &who, stake)?; + let juror = Juror { status: JurorStatus::Ok }; + Jurors::::insert(&who, juror.clone()); + Self::deposit_event(Event::JoinedJuror(who, juror)); Ok(()) } - // MARK(non-transactional): `remove_juror_from_all_courts_of_all_markets` is infallible. #[pallet::weight(T::WeightInfo::exit_court())] + #[transactional] pub fn exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let juror = Self::juror(&who)?; @@ -187,50 +188,162 @@ mod pallet { Ok(()) } - // MARK(non-transactional): Once `reserve_named` is successful, `insert` won't fail. - #[pallet::weight(T::WeightInfo::join_court())] - pub fn join_court(origin: OriginFor) -> DispatchResult { + #[pallet::weight(T::WeightInfo::vote())] + #[transactional] + pub fn vote( + origin: OriginFor, + #[pallet::compact] market_id: MarketIdOf, + secret_vote: T::Hash, + ) -> DispatchResult { let who = ensure_signed(origin)?; - if Jurors::::get(&who).is_some() { - return Err(Error::::JurorAlreadyExists.into()); + if Jurors::::get(&who).is_none() { + return Err(Error::::OnlyJurorsCanVote.into()); } - let jurors_num = Jurors::::count() as usize; - let jurors_num_plus_one = jurors_num.checked_add(1).ok_or(ArithmeticError::Overflow)?; - let stake = Self::current_required_stake(jurors_num_plus_one); - CurrencyOf::::reserve_named(&Self::reserve_id(), &who, stake)?; - let juror = Juror { status: JurorStatus::Ok }; - Jurors::::insert(&who, juror.clone()); - Self::deposit_event(Event::JoinedJuror(who, juror)); + + let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let now = >::block_number(); + ensure!( + court.periods.crowdfund_end < now && now <= court.periods.vote_end, + Error::::NotInVotingPeriod + ); + + let vote = Vote::Secret { secret: secret_vote }; + Votes::::insert(market_id, who, vote); Ok(()) } - // MARK(non-transactional): No fallible storage operation is performed. + // TODO benchmark #[pallet::weight(T::WeightInfo::vote())] - pub fn vote( + #[transactional] + pub fn reveal_vote( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, outcome: OutcomeReport, + salt: T::Hash, ) -> DispatchResult { let who = ensure_signed(origin)?; if Jurors::::get(&who).is_none() { - return Err(Error::::OnlyJurorsCanVote.into()); + return Err(Error::::OnlyJurorsCanReveal.into()); } - Votes::::insert( - market_id, - who, - (>::block_number(), outcome), + let vote = >::get(market_id, &who).ok_or(Error::::NoVoteFound)?; + let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let now = >::block_number(); + ensure!( + court.periods.vote_end < now && now <= court.periods.aggregation_end, + Error::::NotInAggregationPeriod + ); + + // TODO maybe check here if fund amount does fulfill the required stake + let fund_amount = T::Crowdfund::get_item(court.crowdfund_info.index, &outcome) + .ok_or(Error::::InvalidCrowdfundItem)?; + ensure!( + fund_amount >= court.crowdfund_info.threshold, + Error::::OutcomeCrowdfundsBelowThreshold + ); + + let secret = match vote { + Vote::Secret { secret } => { + ensure!( + secret == T::Hashing::hash_of(&(who, market_id, outcome, salt)), + Error::::InvalidReveal + ); + secret + } + _ => return Err(Error::::VoteIsNotSecret.into()), + }; + + let raw_vote = Vote::Revealed { secret, outcome, salt }; + Votes::::insert(market_id, who, raw_vote); + Ok(()) + } + + // TODO benchmark + #[pallet::weight(1_000_000_000_000)] + #[transactional] + pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + ensure_signed(origin)?; + let market = T::MarketCommons::market(&market_id)?; + ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + Error::::MarketDoesNotHaveCourtMechanism + ); + + let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let now = >::block_number(); + + ensure!( + court.periods.aggregation_end < now && now <= court.periods.appeal_end, + Error::::NotInAppealPeriod ); + + ensure!((court.appeals as u32) < T::MaxAppeals::get(), Error::::MaxAppealsReached); + + let iter = T::Crowdfund::iter_items(court.crowdfund_info.index); + let mut count = 0u32; + let mut funded_outcomes = Vec::new(); + let threshold = court.crowdfund_info.threshold; + for (outcome, crowdfund_amount) in iter { + if crowdfund_amount >= threshold { + funded_outcomes.push(outcome); + count = count.saturating_add(1); + } + } + ensure!(count >= 2, Error::::NotEnoughCrowdfundBackingToAppeal); + + let jurors: Vec<_> = Jurors::::iter().collect(); + let necessary_jurors_num = Self::necessary_jurors_num(court.appeals as usize); + let mut rng = Self::rng(); + let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); + for (ai, _) in random_jurors { + RequestedJurors::::insert(market_id, ai, ()); + } + + let last_resolve_at = court.periods.appeal_end; + let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + + let periods = Periods { + crowdfund_end: T::CourtCrowdfundPeriod::get(), + vote_end: T::CourtVotePeriod::get(), + aggregation_end: T::CourtAggregationPeriod::get(), + appeal_end: T::CourtAppealPeriod::get(), + }; + // sets periods one after the other from now + CourtInfo::appeal(&mut court, periods, now); + + let _ids_len_1 = + T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; + + >::insert(market_id, court); + + Self::deposit_event(Event::MarketAppealed { market_id }); + Ok(()) } } #[pallet::config] pub trait Config: frame_system::Config { - /// Block duration to cast a vote on an outcome. #[pallet::constant] - type CourtCaseDuration: Get; + type CourtCrowdfundPeriod: Get; + + #[pallet::constant] + type CourtVotePeriod: Get; + + #[pallet::constant] + type CourtAggregationPeriod: Get; + + #[pallet::constant] + type CourtAppealPeriod: Get; + + type Crowdfund: crate::CrowdfundPalletApi< + Self::AccountId, + BalanceOf, + NegativeImbalanceOf, + >; - type Crowdfund: crate::CrowdfundPalletApi, NegativeImbalanceOf>; + #[pallet::constant] + type CrowdfundMinThreshold: Get>; type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, @@ -248,6 +361,10 @@ mod pallet { BlockNumber = Self::BlockNumber, >; + /// The maximum number of appeals until the court fails. + #[pallet::constant] + type MaxAppeals: Get; + /// Identifier of this pallet #[pallet::constant] type PalletId: Get; @@ -281,6 +398,32 @@ mod pallet { OnlyJurorsCanVote, /// The market is not in a state where it can be disputed. MarketIsNotDisputed, + /// Only jurors can reveal their votes. + OnlyJurorsCanReveal, + /// The vote was not found. + NoVoteFound, + /// The vote is not secret. + VoteIsNotSecret, + /// The outcome and salt reveal do not match the secret vote. + InvalidReveal, + /// The revealed vote outcome was not crowdfunded. + InvalidCrowdfundItem, + /// No court for this market id was found. + CourtNotFound, + /// This operation is only allowed in the voting period. + NotInVotingPeriod, + /// This operation is only allowed in the aggregation period. + NotInAggregationPeriod, + /// There is not enough crowdfund backing to appeal. + NotEnoughCrowdfundBackingToAppeal, + /// The maximum number of appeals has been reached. + MaxAppealsReached, + /// This operation is only allowed in the appeal period. + NotInAppealPeriod, + /// The court is already present for this market. + CourtAlreadyExists, + /// The revealed outcome is below the minimum threshold for the crowdfund. + OutcomeCrowdfundsBelowThreshold, } #[pallet::event] @@ -478,11 +621,8 @@ mod pallet { // // Result is capped to `usize::MAX` or in other words, capped to a very, very, very // high number of jurors. - fn necessary_jurors_num( - disputes: &[MarketDispute>], - ) -> usize { - let len = disputes.len(); - INITIAL_JURORS_NUM.saturating_add(SUBSEQUENT_JURORS_FACTOR.saturating_mul(len)) + fn necessary_jurors_num(appeals_len: usize) -> usize { + INITIAL_JURORS_NUM.saturating_add(SUBSEQUENT_JURORS_FACTOR.saturating_mul(appeals_len)) } // Every juror that not voted on the first or second most voted outcome are slashed. @@ -599,12 +739,35 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_dispute(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); + ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); + + let now = >::block_number(); + let index = T::Crowdfund::open_crowdfund()?; + + let crowdfund_info = + CrowdfundInfo { index, threshold: T::CrowdfundMinThreshold::get() }; + + let periods = Periods { + crowdfund_end: T::CourtCrowdfundPeriod::get(), + vote_end: T::CourtVotePeriod::get(), + aggregation_end: T::CourtAggregationPeriod::get(), + appeal_end: T::CourtAppealPeriod::get(), + }; + + // sets periods one after the other from now + let court = CourtInfo::new(crowdfund_info, now, periods); + + let _ids_len = + T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; + + >::insert(market_id, court); + Ok(()) } @@ -622,10 +785,10 @@ mod pallet { ); let votes: Vec<_> = Votes::::iter_prefix(market_id).collect(); let requested_jurors: Vec<_> = RequestedJurors::::iter_prefix(market_id) - .map(|(juror_id, max_allowed_block)| { + .map(|(juror_id, ())| { let juror = Self::juror(&juror_id)?; let vote_opt = votes.iter().find(|el| el.0 == juror_id).map(|el| &el.1); - Ok((juror_id, juror, max_allowed_block, vote_opt)) + Ok((juror_id, juror, vote_opt)) }) .collect::>()?; let (first, second_opt) = Self::two_best_outcomes(&votes)?; @@ -655,22 +818,31 @@ mod pallet { } fn get_auto_resolve( - _: &Self::MarketId, + market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - Ok(None) + + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + + Ok(Some(court.periods.appeal_end)) } - fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { + fn has_failed( + market_id: &Self::MarketId, + market: &MarketOf, + ) -> Result { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - Ok(false) + + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + + Ok(court.appeals as u32 >= T::MaxAppeals::get()) } fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { @@ -704,14 +876,8 @@ mod pallet { /// Selected jurors that should vote a market outcome until a certain block number #[pallet::storage] - pub type RequestedJurors = StorageDoubleMap< - _, - Blake2_128Concat, - MarketIdOf, - Blake2_128Concat, - T::AccountId, - T::BlockNumber, - >; + pub type RequestedJurors = + StorageDoubleMap<_, Blake2_128Concat, MarketIdOf, Blake2_128Concat, T::AccountId, ()>; /// Votes of market outcomes for disputes /// @@ -723,6 +889,10 @@ mod pallet { MarketIdOf, Blake2_128Concat, T::AccountId, - (T::BlockNumber, OutcomeReport), + Vote, >; + + #[pallet::storage] + pub type Courts = + StorageMap<_, Blake2_128Concat, MarketIdOf, CourtOf, OptionQuery>; } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs new file mode 100644 index 000000000..2f4c01e50 --- /dev/null +++ b/zrml/court/src/types.rs @@ -0,0 +1,143 @@ +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use zeitgeist_primitives::types::OutcomeReport; + +// Structure currently has only one field but acts as a container for possible future additions. +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct Juror { + pub(crate) status: JurorStatus, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub enum JurorStatus { + Ok, + Tardy, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub enum Vote { + Secret { secret: Hash }, + Revealed { secret: Hash, outcome: OutcomeReport, salt: Hash }, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct CrowdfundInfo { + pub(crate) index: u128, + pub(crate) threshold: Balance, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct Periods { + pub(crate) crowdfund_end: BlockNumber, + pub(crate) vote_end: BlockNumber, + pub(crate) aggregation_end: BlockNumber, + pub(crate) appeal_end: BlockNumber, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct CourtInfo { + pub(crate) crowdfund_info: CrowdfundInfo, + pub(crate) appeals: u8, + pub(crate) winner: Option, + pub(crate) periods: Periods, +} + +impl + CourtInfo +{ + pub fn new( + crowdfund_info: CrowdfundInfo, + now: BlockNumber, + periods: Periods, + ) -> Self { + let crowdfund_end = now.saturating_add(periods.crowdfund_end); + let vote_end = crowdfund_end.saturating_add(periods.vote_end); + let aggregation_end = vote_end.saturating_add(periods.aggregation_end); + let appeal_end = aggregation_end.saturating_add(periods.appeal_end); + let periods = Periods { crowdfund_end, vote_end, aggregation_end, appeal_end }; + Self { crowdfund_info, appeals: 0, winner: None, periods } + } + + pub fn appeal(&mut self, periods: Periods, now: BlockNumber) { + // inc the appeal count + self.appeals = self.appeals.saturating_add(1); + // crowdfund threshold + self.crowdfund_info.threshold = + self.crowdfund_info.threshold.saturating_add(self.crowdfund_info.threshold); + // periods + self.periods.crowdfund_end = now.saturating_add(periods.crowdfund_end); + self.periods.vote_end = self.periods.crowdfund_end.saturating_add(periods.vote_end); + self.periods.aggregation_end = + self.periods.vote_end.saturating_add(periods.aggregation_end); + self.periods.appeal_end = self.periods.aggregation_end.saturating_add(periods.appeal_end); + } +} From edc05a10578da049cc8bb332bfa52ba23a5fee5c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 16 Feb 2023 13:36:28 +0100 Subject: [PATCH 086/368] prepare crowdfund refunds --- zrml/court/src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7717e866f..febe7c87a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -121,7 +121,7 @@ mod pallet { use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng}; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating}, - ArithmeticError, DispatchError, SaturatedConversion, + ArithmeticError, DispatchError, Percent, SaturatedConversion, }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, @@ -800,6 +800,19 @@ mod pallet { Self::slash_losers_to_award_winners(&valid_winners_and_losers, &first)?; let _ = Votes::::clear_prefix(market_id, u32::max_value(), None); let _ = RequestedJurors::::clear_prefix(market_id, u32::max_value(), None); + + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + + for (outcome, _crowdfund_amount) in T::Crowdfund::iter_items(court.crowdfund_info.index) + { + T::Crowdfund::prepare_refund( + court.crowdfund_info.index, + &outcome, + Percent::zero(), + )?; + } + T::Crowdfund::close_crowdfund(court.crowdfund_info.index)?; + Ok(Some(first)) } From a7f32627f83dcb8b2861c7d17ac5f9ec20eadbb7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 16 Feb 2023 14:19:07 +0100 Subject: [PATCH 087/368] insert appeal info --- zrml/court/src/lib.rs | 27 ++++++++++++++++----------- zrml/court/src/types.rs | 23 ++++++++++++++++++++--- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index febe7c87a..6087196e6 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -277,7 +277,10 @@ mod pallet { Error::::NotInAppealPeriod ); - ensure!((court.appeals as u32) < T::MaxAppeals::get(), Error::::MaxAppealsReached); + ensure!( + court.appeal_info.current < court.appeal_info.max, + Error::::MaxAppealsReached + ); let iter = T::Crowdfund::iter_items(court.crowdfund_info.index); let mut count = 0u32; @@ -291,14 +294,6 @@ mod pallet { } ensure!(count >= 2, Error::::NotEnoughCrowdfundBackingToAppeal); - let jurors: Vec<_> = Jurors::::iter().collect(); - let necessary_jurors_num = Self::necessary_jurors_num(court.appeals as usize); - let mut rng = Self::rng(); - let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); - for (ai, _) in random_jurors { - RequestedJurors::::insert(market_id, ai, ()); - } - let last_resolve_at = court.periods.appeal_end; let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); @@ -309,7 +304,17 @@ mod pallet { appeal_end: T::CourtAppealPeriod::get(), }; // sets periods one after the other from now - CourtInfo::appeal(&mut court, periods, now); + court.appeal(periods, now); + + let jurors: Vec<_> = Jurors::::iter().collect(); + let current_appeals = court.appeal_info.current as usize; + let necessary_jurors_num = + Self::necessary_jurors_num(current_appeals); + let mut rng = Self::rng(); + let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); + for (ai, _) in random_jurors { + RequestedJurors::::insert(market_id, ai, ()); + } let _ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; @@ -761,7 +766,7 @@ mod pallet { }; // sets periods one after the other from now - let court = CourtInfo::new(crowdfund_info, now, periods); + let court = CourtInfo::new(crowdfund_info, now, periods, T::MaxAppeals::get() as u8); let _ids_len = T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 2f4c01e50..54a8d81e2 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -94,6 +94,21 @@ pub struct Periods { pub(crate) appeal_end: BlockNumber, } +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct AppealInfo { + pub(crate) current: u8, + pub(crate) max: u8, +} + #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -106,7 +121,7 @@ pub struct Periods { )] pub struct CourtInfo { pub(crate) crowdfund_info: CrowdfundInfo, - pub(crate) appeals: u8, + pub(crate) appeal_info: AppealInfo, pub(crate) winner: Option, pub(crate) periods: Periods, } @@ -118,18 +133,20 @@ impl, now: BlockNumber, periods: Periods, + max_appeals: u8, ) -> Self { let crowdfund_end = now.saturating_add(periods.crowdfund_end); let vote_end = crowdfund_end.saturating_add(periods.vote_end); let aggregation_end = vote_end.saturating_add(periods.aggregation_end); let appeal_end = aggregation_end.saturating_add(periods.appeal_end); let periods = Periods { crowdfund_end, vote_end, aggregation_end, appeal_end }; - Self { crowdfund_info, appeals: 0, winner: None, periods } + let appeal_info = AppealInfo { current: 0, max: max_appeals }; + Self { crowdfund_info, appeal_info, winner: None, periods } } pub fn appeal(&mut self, periods: Periods, now: BlockNumber) { // inc the appeal count - self.appeals = self.appeals.saturating_add(1); + self.appeal_info.current = self.appeal_info.current.saturating_add(1); // crowdfund threshold self.crowdfund_info.threshold = self.crowdfund_info.threshold.saturating_add(self.crowdfund_info.threshold); From aa88d40aa0210b28ee75e185a4940c7435fb62c0 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 17 Feb 2023 09:17:39 +0100 Subject: [PATCH 088/368] split appeal work --- zrml/court/src/lib.rs | 139 +++++++++++++++++++++++++++++----------- zrml/court/src/types.rs | 14 ++-- 2 files changed, 113 insertions(+), 40 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 6087196e6..88708cd01 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -233,7 +233,6 @@ mod pallet { Error::::NotInAggregationPeriod ); - // TODO maybe check here if fund amount does fulfill the required stake let fund_amount = T::Crowdfund::get_item(court.crowdfund_info.index, &outcome) .ok_or(Error::::InvalidCrowdfundItem)?; ensure!( @@ -243,8 +242,13 @@ mod pallet { let secret = match vote { Vote::Secret { secret } => { + // market id and current appeal number is part of salt generation + // salt should be signed by the juror (market_id ++ appeal number) + // salt can be reproduced only be the juror address + // with knowing market_id and appeal number + // so even if the salt is forgotten it can be reproduced only by the juror ensure!( - secret == T::Hashing::hash_of(&(who, market_id, outcome, salt)), + secret == T::Hashing::hash_of(&(who, outcome, salt)), Error::::InvalidReveal ); secret @@ -260,40 +264,77 @@ mod pallet { // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] - pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + pub fn check_crowdfund(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { ensure_signed(origin)?; - let market = T::MarketCommons::market(&market_id)?; - ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Court, - Error::::MarketDoesNotHaveCourtMechanism - ); let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + ensure!(!court.appeal_info.is_funded, Error::::AppealAlreadyFunded); let now = >::block_number(); + Self::check_appealable_market(&market_id, &court, now)?; - ensure!( - court.periods.aggregation_end < now && now <= court.periods.appeal_end, - Error::::NotInAppealPeriod - ); - - ensure!( - court.appeal_info.current < court.appeal_info.max, - Error::::MaxAppealsReached - ); + // update crowdfund threshold + let threshold = + court.crowdfund_info.threshold.saturating_add(court.crowdfund_info.threshold); - let iter = T::Crowdfund::iter_items(court.crowdfund_info.index); let mut count = 0u32; - let mut funded_outcomes = Vec::new(); - let threshold = court.crowdfund_info.threshold; - for (outcome, crowdfund_amount) in iter { + // TODO: use iter_from https://paritytech.github.io/substrate/master/frame_support/pallet_prelude/struct.StorageMap.html#method.iter_from + // TODO: with iter_from we can iterate from the last checked item (weight restrictions) + for (outcome, crowdfund_amount) in T::Crowdfund::iter_items(court.crowdfund_info.index) + { if crowdfund_amount >= threshold { - funded_outcomes.push(outcome); count = count.saturating_add(1); + if count >= 2 { + break; + } } } ensure!(count >= 2, Error::::NotEnoughCrowdfundBackingToAppeal); + court.crowdfund_info.threshold = threshold; + court.appeal_info.is_funded = true; + >::insert(&market_id, court); + + Ok(()) + } + + // TODO benchmark + #[pallet::weight(1_000_000_000_000)] + #[transactional] + pub fn draw_jurors(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + ensure_signed(origin)?; + + let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + ensure!(!court.appeal_info.is_drawn, Error::::JurorsAlreadyDrawn); + ensure!(court.appeal_info.is_funded, Error::::CheckCrowdfundFirst); + let now = >::block_number(); + Self::check_appealable_market(&market_id, &court, now)?; + + let jurors: Vec<_> = Jurors::::iter().collect(); + let current_appeals = court.appeal_info.current as usize; + let necessary_jurors_num = Self::necessary_jurors_num(current_appeals); + let mut rng = Self::rng(); + let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); + for (ai, _) in random_jurors { + RequestedJurors::::insert(market_id, ai, ()); + } + + court.appeal_info.is_drawn = true; + >::insert(&market_id, court); + + Ok(()) + } + + // TODO benchmark + #[pallet::weight(1_000_000_000_000)] + #[transactional] + pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + ensure_signed(origin)?; + + let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + ensure!(court.appeal_info.is_appeal_ready(), Error::::AppealNotReady); + let now = >::block_number(); + Self::check_appealable_market(&market_id, &court, now)?; + let last_resolve_at = court.periods.appeal_end; let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); @@ -306,16 +347,6 @@ mod pallet { // sets periods one after the other from now court.appeal(periods, now); - let jurors: Vec<_> = Jurors::::iter().collect(); - let current_appeals = court.appeal_info.current as usize; - let necessary_jurors_num = - Self::necessary_jurors_num(current_appeals); - let mut rng = Self::rng(); - let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); - for (ai, _) in random_jurors { - RequestedJurors::::insert(market_id, ai, ()); - } - let _ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; @@ -429,6 +460,10 @@ mod pallet { CourtAlreadyExists, /// The revealed outcome is below the minimum threshold for the crowdfund. OutcomeCrowdfundsBelowThreshold, + JurorsAlreadyDrawn, + AppealAlreadyFunded, + CheckCrowdfundFirst, + AppealNotReady, } #[pallet::event] @@ -456,6 +491,31 @@ mod pallet { where T: Config, { + pub(crate) fn check_appealable_market( + market_id: &MarketIdOf, + court: &CourtOf, + now: T::BlockNumber, + ) -> Result<(), DispatchError> { + let market = T::MarketCommons::market(&market_id)?; + ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + Error::::MarketDoesNotHaveCourtMechanism + ); + + ensure!( + court.periods.aggregation_end < now && now <= court.periods.appeal_end, + Error::::NotInAppealPeriod + ); + + ensure!( + court.appeal_info.current < court.appeal_info.max, + Error::::MaxAppealsReached + ); + + Ok(()) + } + // Returns an unique random subset of `jurors` with length `len`. // // If `len` is greater than the length of `jurors`, then `len` will be capped. @@ -768,6 +828,15 @@ mod pallet { // sets periods one after the other from now let court = CourtInfo::new(crowdfund_info, now, periods, T::MaxAppeals::get() as u8); + let jurors: Vec<_> = Jurors::::iter().collect(); + // appeal number is zero (0usize) at the beginning + let necessary_jurors_num = Self::necessary_jurors_num(0usize); + let mut rng = Self::rng(); + let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); + for (ai, _) in random_jurors { + RequestedJurors::::insert(market_id, ai, ()); + } + let _ids_len = T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; @@ -845,7 +914,6 @@ mod pallet { ); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - Ok(Some(court.periods.appeal_end)) } @@ -859,8 +927,7 @@ mod pallet { ); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - - Ok(court.appeals as u32 >= T::MaxAppeals::get()) + Ok(court.appeal_info.current >= court.appeal_info.max) } fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 54a8d81e2..e11344d4a 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -107,6 +107,14 @@ pub struct Periods { pub struct AppealInfo { pub(crate) current: u8, pub(crate) max: u8, + pub(crate) is_drawn: bool, + pub(crate) is_funded: bool, +} + +impl AppealInfo { + pub fn is_appeal_ready(&self) -> bool { + self.is_drawn && self.is_funded + } } #[derive( @@ -140,16 +148,14 @@ impl, now: BlockNumber) { // inc the appeal count self.appeal_info.current = self.appeal_info.current.saturating_add(1); - // crowdfund threshold - self.crowdfund_info.threshold = - self.crowdfund_info.threshold.saturating_add(self.crowdfund_info.threshold); // periods self.periods.crowdfund_end = now.saturating_add(periods.crowdfund_end); self.periods.vote_end = self.periods.crowdfund_end.saturating_add(periods.vote_end); From daac99bd3ed7c6e7534d769653818773fd9e0529 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 17 Feb 2023 11:30:01 +0100 Subject: [PATCH 089/368] update pallet structure --- zrml/court/src/lib.rs | 429 +++++++++++++++++++++------------------- zrml/court/src/types.rs | 8 +- 2 files changed, 226 insertions(+), 211 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 88708cd01..4e71bc1a9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -112,7 +112,7 @@ mod pallet { CountedStorageMap, OptionQuery, StorageDoubleMap, StorageMap, StorageValue, ValueQuery, }, traits::{ - BalanceStatus, Currency, Get, Hooks, IsType, NamedReservableCurrency, Randomness, + BalanceStatus, Currency, Get, IsType, NamedReservableCurrency, Randomness, StorageVersion, }, transactional, Blake2_128Concat, PalletId, @@ -129,6 +129,68 @@ mod pallet { }; use zrml_market_commons::MarketCommonsPalletApi; + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + type CourtCrowdfundPeriod: Get; + + #[pallet::constant] + type CourtVotePeriod: Get; + + #[pallet::constant] + type CourtAggregationPeriod: Get; + + #[pallet::constant] + type CourtAppealPeriod: Get; + + type Crowdfund: crate::CrowdfundPalletApi< + Self::AccountId, + BalanceOf, + NegativeImbalanceOf, + >; + + #[pallet::constant] + type CrowdfundMinThreshold: Get>; + + type DisputeResolution: DisputeResolutionApi< + AccountId = Self::AccountId, + BlockNumber = Self::BlockNumber, + MarketId = MarketIdOf, + Moment = MomentOf, + >; + + /// Event + type Event: From> + IsType<::Event>; + + /// Market commons + type MarketCommons: MarketCommonsPalletApi< + AccountId = Self::AccountId, + BlockNumber = Self::BlockNumber, + >; + + /// The maximum number of appeals until the court fails. + #[pallet::constant] + type MaxAppeals: Get; + + /// Identifier of this pallet + #[pallet::constant] + type PalletId: Get; + + /// Randomness source + type Random: Randomness; + + /// Weight used to calculate the necessary staking amount to become a juror + #[pallet::constant] + type StakeWeight: Get>; + + /// Slashed funds are send to the treasury + #[pallet::constant] + type TreasuryPalletId: Get; + + /// Weights generated by benchmarks + type WeightInfo: WeightInfoZeitgeist; + } + // Number of jurors for an initial market dispute const INITIAL_JURORS_NUM: usize = 3; const MAX_RANDOM_JURORS: usize = 13; @@ -159,6 +221,106 @@ mod pallet { >; pub(crate) type CourtOf = CourtInfo, ::BlockNumber>; + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData); + + /// Accounts that stake funds to decide outcomes. + #[pallet::storage] + pub type Jurors = CountedStorageMap<_, Blake2_128Concat, T::AccountId, Juror>; + + /// An extra layer of pseudo randomness. + #[pallet::storage] + pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; + + #[pallet::storage] + pub type RequestedVotes = StorageDoubleMap< + _, + Blake2_128Concat, + MarketIdOf, + Blake2_128Concat, + T::AccountId, + Vote, + >; + + #[pallet::storage] + pub type Courts = + StorageMap<_, Blake2_128Concat, MarketIdOf, CourtOf, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(fn deposit_event)] + pub enum Event + where + T: Config, + { + /// A juror has been added to the court. + JoinedJuror(T::AccountId, Juror), + /// A juror has been removed from the court. + ExitedJuror(T::AccountId, Juror), + /// A juror has voted in a court. + JurorVoted { market_id: MarketIdOf, juror: T::AccountId, secret: T::Hash }, + /// A juror has revealed their vote. + JurorRevealedVote { + juror: T::AccountId, + market_id: MarketIdOf, + outcome: OutcomeReport, + salt: T::Hash, + }, + /// The jurors for an appeal have been drawn. + AppealJurorsDrawn { market_id: MarketIdOf }, + /// The crowdfund for an appeal has been checked. + AppealCrowdfundChecked { market_id: MarketIdOf }, + /// A market has been appealed. + MarketAppealed { market_id: MarketIdOf, appeal_number: u8 }, + } + + #[pallet::error] + pub enum Error { + /// It is not possible to insert a Juror that is already stored + JurorAlreadyExists, + /// An account id does not exist on the jurors storage. + JurorDoesNotExists, + /// On dispute or resolution, someone tried to pass a non-court market type + MarketDoesNotHaveCourtMechanism, + /// No-one voted on an outcome to resolve a market + NoVotes, + /// Forbids voting of unknown accounts + OnlyJurorsCanVote, + /// The market is not in a state where it can be disputed. + MarketIsNotDisputed, + /// Only jurors can reveal their votes. + OnlyJurorsCanReveal, + /// The vote was not found. + NoVoteFound, + /// The vote is not secret. + VoteIsNotSecret, + /// The outcome and salt reveal do not match the secret vote. + InvalidReveal, + /// The revealed vote outcome was not crowdfunded. + InvalidCrowdfundItem, + /// No court for this market id was found. + CourtNotFound, + /// This operation is only allowed in the voting period. + NotInVotingPeriod, + /// This operation is only allowed in the aggregation period. + NotInAggregationPeriod, + /// There is not enough crowdfund backing to appeal. + NotEnoughCrowdfundBackingToAppeal, + /// The maximum number of appeals has been reached. + MaxAppealsReached, + /// This operation is only allowed in the appeal period. + NotInAppealPeriod, + /// The court is already present for this market. + CourtAlreadyExists, + /// The revealed outcome is below the minimum threshold for the crowdfund. + OutcomeCrowdfundsBelowThreshold, + JurorsAlreadyDrawn, + AppealAlreadyFunded, + CheckCrowdfundFirst, + AppealNotReady, + OnlyDrawnJurorsCanVote, + } + #[pallet::call] impl Pallet { #[pallet::weight(T::WeightInfo::join_court())] @@ -196,9 +358,12 @@ mod pallet { secret_vote: T::Hash, ) -> DispatchResult { let who = ensure_signed(origin)?; - if Jurors::::get(&who).is_none() { - return Err(Error::::OnlyJurorsCanVote.into()); - } + + ensure!(>::contains_key(&who), Error::::OnlyJurorsCanVote); + ensure!( + >::contains_key(&market_id, &who), + Error::::OnlyDrawnJurorsCanVote + ); let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -208,7 +373,9 @@ mod pallet { ); let vote = Vote::Secret { secret: secret_vote }; - Votes::::insert(market_id, who, vote); + RequestedVotes::::insert(market_id, who, vote); + + Self::deposit_event(Event::JurorVoted { juror: who, market_id, secret: secret_vote }); Ok(()) } @@ -225,7 +392,7 @@ mod pallet { if Jurors::::get(&who).is_none() { return Err(Error::::OnlyJurorsCanReveal.into()); } - let vote = >::get(market_id, &who).ok_or(Error::::NoVoteFound)?; + let vote = >::get(market_id, &who).ok_or(Error::::NoVoteFound)?; let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( @@ -257,14 +424,19 @@ mod pallet { }; let raw_vote = Vote::Revealed { secret, outcome, salt }; - Votes::::insert(market_id, who, raw_vote); + RequestedVotes::::insert(market_id, who, raw_vote); + + Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); Ok(()) } // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] - pub fn check_crowdfund(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + pub fn check_appeal_crowdfund( + origin: OriginFor, + market_id: MarketIdOf, + ) -> DispatchResult { ensure_signed(origin)?; let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; @@ -294,13 +466,18 @@ mod pallet { court.appeal_info.is_funded = true; >::insert(&market_id, court); + Self::deposit_event(Event::AppealCrowdfundChecked { market_id }); + Ok(()) } // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] - pub fn draw_jurors(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + pub fn draw_appeal_jurors( + origin: OriginFor, + market_id: MarketIdOf, + ) -> DispatchResult { ensure_signed(origin)?; let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; @@ -309,18 +486,14 @@ mod pallet { let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - let jurors: Vec<_> = Jurors::::iter().collect(); - let current_appeals = court.appeal_info.current as usize; - let necessary_jurors_num = Self::necessary_jurors_num(current_appeals); - let mut rng = Self::rng(); - let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); - for (ai, _) in random_jurors { - RequestedJurors::::insert(market_id, ai, ()); - } + let appeal_number = court.appeal_info.current as usize; + Self::select_jurors(&market_id, appeal_number); court.appeal_info.is_drawn = true; >::insert(&market_id, court); + Self::deposit_event(Event::AppealJurorsDrawn { market_id }); + Ok(()) } @@ -345,152 +518,37 @@ mod pallet { appeal_end: T::CourtAppealPeriod::get(), }; // sets periods one after the other from now - court.appeal(periods, now); + court.update_periods(periods, now); + court.appeal_info.current = court.appeal_info.current.saturating_add(1); let _ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; >::insert(market_id, court); - Self::deposit_event(Event::MarketAppealed { market_id }); + Self::deposit_event(Event::MarketAppealed { + market_id, + appeal_number: court.appeal_info.current, + }); Ok(()) } } - #[pallet::config] - pub trait Config: frame_system::Config { - #[pallet::constant] - type CourtCrowdfundPeriod: Get; - - #[pallet::constant] - type CourtVotePeriod: Get; - - #[pallet::constant] - type CourtAggregationPeriod: Get; - - #[pallet::constant] - type CourtAppealPeriod: Get; - - type Crowdfund: crate::CrowdfundPalletApi< - Self::AccountId, - BalanceOf, - NegativeImbalanceOf, - >; - - #[pallet::constant] - type CrowdfundMinThreshold: Get>; - - type DisputeResolution: DisputeResolutionApi< - AccountId = Self::AccountId, - BlockNumber = Self::BlockNumber, - MarketId = MarketIdOf, - Moment = MomentOf, - >; - - /// Event - type Event: From> + IsType<::Event>; - - /// Market commons - type MarketCommons: MarketCommonsPalletApi< - AccountId = Self::AccountId, - BlockNumber = Self::BlockNumber, - >; - - /// The maximum number of appeals until the court fails. - #[pallet::constant] - type MaxAppeals: Get; - - /// Identifier of this pallet - #[pallet::constant] - type PalletId: Get; - - /// Randomness source - type Random: Randomness; - - /// Weight used to calculate the necessary staking amount to become a juror - #[pallet::constant] - type StakeWeight: Get>; - - /// Slashed funds are send to the treasury - #[pallet::constant] - type TreasuryPalletId: Get; - - /// Weights generated by benchmarks - type WeightInfo: WeightInfoZeitgeist; - } - - #[pallet::error] - pub enum Error { - /// It is not possible to insert a Juror that is already stored - JurorAlreadyExists, - /// An account id does not exist on the jurors storage. - JurorDoesNotExists, - /// On dispute or resolution, someone tried to pass a non-court market type - MarketDoesNotHaveCourtMechanism, - /// No-one voted on an outcome to resolve a market - NoVotes, - /// Forbids voting of unknown accounts - OnlyJurorsCanVote, - /// The market is not in a state where it can be disputed. - MarketIsNotDisputed, - /// Only jurors can reveal their votes. - OnlyJurorsCanReveal, - /// The vote was not found. - NoVoteFound, - /// The vote is not secret. - VoteIsNotSecret, - /// The outcome and salt reveal do not match the secret vote. - InvalidReveal, - /// The revealed vote outcome was not crowdfunded. - InvalidCrowdfundItem, - /// No court for this market id was found. - CourtNotFound, - /// This operation is only allowed in the voting period. - NotInVotingPeriod, - /// This operation is only allowed in the aggregation period. - NotInAggregationPeriod, - /// There is not enough crowdfund backing to appeal. - NotEnoughCrowdfundBackingToAppeal, - /// The maximum number of appeals has been reached. - MaxAppealsReached, - /// This operation is only allowed in the appeal period. - NotInAppealPeriod, - /// The court is already present for this market. - CourtAlreadyExists, - /// The revealed outcome is below the minimum threshold for the crowdfund. - OutcomeCrowdfundsBelowThreshold, - JurorsAlreadyDrawn, - AppealAlreadyFunded, - CheckCrowdfundFirst, - AppealNotReady, - } - - #[pallet::event] - #[pallet::generate_deposit(fn deposit_event)] - pub enum Event - where - T: Config, - { - ExitedJuror(T::AccountId, Juror), - JoinedJuror(T::AccountId, Juror), - /// A market has been appealed. - MarketAppealed { - market_id: MarketIdOf, - }, - } - - #[pallet::hooks] - impl Hooks for Pallet {} - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(PhantomData); - impl Pallet where T: Config, { + pub(crate) fn select_jurors(market_id: &MarketIdOf, appeal_number: usize) { + let jurors: Vec<_> = Jurors::::iter().collect(); + let necessary_jurors_num = Self::necessary_jurors_num(appeal_number); + let mut rng = Self::rng(); + let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); + for (ai, _) in random_jurors { + RequestedVotes::::insert(market_id, ai, Vote::Drawn); + } + } + pub(crate) fn check_appealable_market( market_id: &MarketIdOf, court: &CourtOf, @@ -687,6 +745,7 @@ mod pallet { // Result is capped to `usize::MAX` or in other words, capped to a very, very, very // high number of jurors. fn necessary_jurors_num(appeals_len: usize) -> usize { + // TODO: increase requested juror number higher; current max example: 3 + 2 * MaxAppeals INITIAL_JURORS_NUM.saturating_add(SUBSEQUENT_JURORS_FACTOR.saturating_mul(appeals_len)) } @@ -780,14 +839,11 @@ mod pallet { CurrencyOf::::unreserve_all_named(&Self::reserve_id(), ai); Jurors::::remove(ai); let mut market_ids = BTreeSet::new(); - market_ids.extend(RequestedJurors::::iter().map(|el| el.0)); - for market_id in &market_ids { - RequestedJurors::::remove(market_id, ai); - } - market_ids.clear(); - market_ids.extend(Votes::::iter().map(|el| el.0)); + market_ids.extend(RequestedVotes::::iter().map(|el| el.0)); for market_id in &market_ids { - Votes::::remove(market_id, ai); + // TODO: should we allow removing all vote requests? + // TODO: because the juror could avoid getting slashed by just exit_court + RequestedVotes::::remove(market_id, ai); } } } @@ -828,14 +884,7 @@ mod pallet { // sets periods one after the other from now let court = CourtInfo::new(crowdfund_info, now, periods, T::MaxAppeals::get() as u8); - let jurors: Vec<_> = Jurors::::iter().collect(); - // appeal number is zero (0usize) at the beginning - let necessary_jurors_num = Self::necessary_jurors_num(0usize); - let mut rng = Self::rng(); - let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); - for (ai, _) in random_jurors { - RequestedJurors::::insert(market_id, ai, ()); - } + Self::select_jurors(market_id, 0usize); let _ids_len = T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; @@ -857,26 +906,20 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - let votes: Vec<_> = Votes::::iter_prefix(market_id).collect(); - let requested_jurors: Vec<_> = RequestedJurors::::iter_prefix(market_id) - .map(|(juror_id, ())| { - let juror = Self::juror(&juror_id)?; - let vote_opt = votes.iter().find(|el| el.0 == juror_id).map(|el| &el.1); - Ok((juror_id, juror, vote_opt)) - }) - .collect::>()?; - let (first, second_opt) = Self::two_best_outcomes(&votes)?; + let votes: Vec<_> = RequestedVotes::::iter_prefix(market_id).collect(); + let (first, second_opt) = Self::two_best_outcomes(votes.as_slice())?; let valid_winners_and_losers = if let Some(second) = second_opt { Self::manage_tardy_jurors(&requested_jurors, |outcome| outcome == &second)? } else { Self::manage_tardy_jurors(&requested_jurors, |_| false)? }; Self::slash_losers_to_award_winners(&valid_winners_and_losers, &first)?; - let _ = Votes::::clear_prefix(market_id, u32::max_value(), None); - let _ = RequestedJurors::::clear_prefix(market_id, u32::max_value(), None); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + // TODO: use an own API call instead, which allows to prepare the refund for all inside the crowdfund pallet (call extrinsic multiple times) + // TODO: specify fees somehow for specific outcomes in this api call + // TODO: the reason for this is that there are weight limitations here (storage iter) for (outcome, _crowdfund_amount) in T::Crowdfund::iter_items(court.crowdfund_info.index) { T::Crowdfund::prepare_refund( @@ -887,6 +930,9 @@ mod pallet { } T::Crowdfund::close_crowdfund(court.crowdfund_info.index)?; + // this only removes a maximum of jurors according to MaxAppeals (weight limited) + let _ = RequestedVotes::::clear_prefix(market_id, u32::max_value(), None); + Ok(Some(first)) } @@ -943,41 +989,12 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - let _ = Votes::::clear_prefix(market_id, u32::max_value(), None); - let _ = RequestedJurors::::clear_prefix(market_id, u32::max_value(), None); + // only removes a maximum of jurors according to MaxAppeals (weight limited) + let _ = RequestedVotes::::clear_prefix(market_id, u32::max_value(), None); + >::remove(market_id); Ok(()) } } impl CourtPalletApi for Pallet where T: Config {} - - /// Accounts that stake funds to decide outcomes. - #[pallet::storage] - pub type Jurors = CountedStorageMap<_, Blake2_128Concat, T::AccountId, Juror>; - - /// An extra layer of pseudo randomness. - #[pallet::storage] - pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; - - /// Selected jurors that should vote a market outcome until a certain block number - #[pallet::storage] - pub type RequestedJurors = - StorageDoubleMap<_, Blake2_128Concat, MarketIdOf, Blake2_128Concat, T::AccountId, ()>; - - /// Votes of market outcomes for disputes - /// - /// Stores the vote block number and the submitted outcome. - #[pallet::storage] - pub type Votes = StorageDoubleMap< - _, - Blake2_128Concat, - MarketIdOf, - Blake2_128Concat, - T::AccountId, - Vote, - >; - - #[pallet::storage] - pub type Courts = - StorageMap<_, Blake2_128Concat, MarketIdOf, CourtOf, OptionQuery>; } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index e11344d4a..c66eb7cfe 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -58,6 +58,7 @@ pub enum JurorStatus { Eq, )] pub enum Vote { + Drawn, Secret { secret: Hash }, Revealed { secret: Hash, outcome: OutcomeReport, salt: Hash }, } @@ -149,14 +150,11 @@ impl, now: BlockNumber) { - // inc the appeal count - self.appeal_info.current = self.appeal_info.current.saturating_add(1); - // periods + pub fn update_periods(&mut self, periods: Periods, now: BlockNumber) { self.periods.crowdfund_end = now.saturating_add(periods.crowdfund_end); self.periods.vote_end = self.periods.crowdfund_end.saturating_add(periods.vote_end); self.periods.aggregation_end = From b1af6535e157ea5292c2611890dc1eb0b28e2e1e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 17 Feb 2023 15:50:15 +0100 Subject: [PATCH 090/368] wip --- zrml/court/src/lib.rs | 353 +++++++++++++++++----------------------- zrml/court/src/types.rs | 20 +-- 2 files changed, 155 insertions(+), 218 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 4e71bc1a9..19efd4fd0 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -35,7 +35,7 @@ pub mod weights; pub use court_pallet_api::CourtPalletApi; pub use pallet::*; -pub use types::{CourtInfo, CrowdfundInfo, Juror, JurorStatus, Periods, Vote}; +pub use types::*; // TODO: remove this crowdfund interface and use the real after crowdfund pallet is merged use frame_support::pallet_prelude::{DispatchError, DispatchResult}; @@ -97,7 +97,7 @@ pub trait CrowdfundPalletApi { mod pallet { use crate::{ weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CrowdfundInfo, CrowdfundPalletApi, - Juror, JurorStatus, Periods, Vote, + JurorInfo, Periods, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -107,21 +107,19 @@ mod pallet { use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, - ensure, - pallet_prelude::{ - CountedStorageMap, OptionQuery, StorageDoubleMap, StorageMap, StorageValue, ValueQuery, - }, + ensure, log, + pallet_prelude::{OptionQuery, StorageDoubleMap, StorageMap, StorageValue, ValueQuery}, traits::{ BalanceStatus, Currency, Get, IsType, NamedReservableCurrency, Randomness, StorageVersion, }, - transactional, Blake2_128Concat, PalletId, + transactional, Blake2_128Concat, BoundedVec, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng}; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating}, - ArithmeticError, DispatchError, Percent, SaturatedConversion, + DispatchError, Percent, }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, @@ -172,6 +170,12 @@ mod pallet { #[pallet::constant] type MaxAppeals: Get; + #[pallet::constant] + type MaxJurors: Get; + + #[pallet::constant] + type MinStake: Get>; + /// Identifier of this pallet #[pallet::constant] type PalletId: Get; @@ -220,6 +224,11 @@ mod pallet { Asset>, >; pub(crate) type CourtOf = CourtInfo, ::BlockNumber>; + pub(crate) type JurorInfoOf = JurorInfo>; + pub(crate) type JurorListOf = BoundedVec< + (BalanceOf, ::AccountId), + ::MaxJurors, + >; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -227,7 +236,11 @@ mod pallet { /// Accounts that stake funds to decide outcomes. #[pallet::storage] - pub type Jurors = CountedStorageMap<_, Blake2_128Concat, T::AccountId, Juror>; + pub type JurorList = StorageValue<_, JurorListOf, ValueQuery>; + + #[pallet::storage] + pub type Jurors = + StorageMap<_, Blake2_128Concat, T::AccountId, JurorInfoOf, OptionQuery>; /// An extra layer of pseudo randomness. #[pallet::storage] @@ -254,9 +267,9 @@ mod pallet { T: Config, { /// A juror has been added to the court. - JoinedJuror(T::AccountId, Juror), + JoinedJuror { juror: T::AccountId }, /// A juror has been removed from the court. - ExitedJuror(T::AccountId, Juror), + ExitedJuror { juror: T::AccountId }, /// A juror has voted in a court. JurorVoted { market_id: MarketIdOf, juror: T::AccountId, secret: T::Hash }, /// A juror has revealed their vote. @@ -276,8 +289,6 @@ mod pallet { #[pallet::error] pub enum Error { - /// It is not possible to insert a Juror that is already stored - JurorAlreadyExists, /// An account id does not exist on the jurors storage. JurorDoesNotExists, /// On dispute or resolution, someone tried to pass a non-court market type @@ -319,24 +330,60 @@ mod pallet { CheckCrowdfundFirst, AppealNotReady, OnlyDrawnJurorsCanVote, + BelowMinStake, + MaxJurorsReached, } #[pallet::call] impl Pallet { #[pallet::weight(T::WeightInfo::join_court())] #[transactional] - pub fn join_court(origin: OriginFor) -> DispatchResult { + pub fn join_court(origin: OriginFor, amount: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; - if Jurors::::get(&who).is_some() { - return Err(Error::::JurorAlreadyExists.into()); + ensure!(amount >= T::MinStake::get(), Error::::BelowMinStake); + + let mut jurors = JurorList::::get(); + + let mut juror_info = JurorInfoOf:: { stake: amount }; + + if let Some(prev_juror_info) = >::get(&who) { + if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + { + jurors.remove(i); + } else { + log::warn!("Join court: Juror stake not found in list"); + debug_assert!(false); + } + + let updated_stake = prev_juror_info.stake.saturating_add(amount); + match jurors.binary_search_by_key(&updated_stake, |tuple| tuple.0) { + Ok(i) => jurors + .try_insert(i, (updated_stake, who.clone())) + .map_err(|_| Error::::MaxJurorsReached)?, + Err(i) => jurors + .try_insert(i, (updated_stake, who.clone())) + .map_err(|_| Error::::MaxJurorsReached)?, + }; + + juror_info.stake = updated_stake; + } else { + match jurors.binary_search_by_key(&amount, |tuple| tuple.0) { + Ok(i) => jurors + .try_insert(i, (amount, who.clone())) + .map_err(|_| Error::::MaxJurorsReached)?, + Err(i) => jurors + .try_insert(i, (amount, who.clone())) + .map_err(|_| Error::::MaxJurorsReached)?, + }; } - let jurors_num = Jurors::::count() as usize; - let jurors_num_plus_one = jurors_num.checked_add(1).ok_or(ArithmeticError::Overflow)?; - let stake = Self::current_required_stake(jurors_num_plus_one); - CurrencyOf::::reserve_named(&Self::reserve_id(), &who, stake)?; - let juror = Juror { status: JurorStatus::Ok }; - Jurors::::insert(&who, juror.clone()); - Self::deposit_event(Event::JoinedJuror(who, juror)); + + CurrencyOf::::reserve_named(&Self::reserve_id(), &who, amount)?; + + JurorList::::put(jurors); + + >::insert(&who, juror_info); + + Self::deposit_event(Event::JoinedJuror { juror: who }); Ok(()) } @@ -344,9 +391,24 @@ mod pallet { #[transactional] pub fn exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let juror = Self::juror(&who)?; + Self::juror(&who)?; + + let mut jurors = JurorList::::get(); + + if let Some(prev_juror_info) = >::get(&who) { + if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + { + jurors.remove(i); + >::put(jurors); + } else { + log::warn!("Exit court: Juror stake not found in list"); + debug_assert!(false); + } + } + Self::remove_juror_from_all_courts_of_all_markets(&who); - Self::deposit_event(Event::ExitedJuror(who, juror)); + + Self::deposit_event(Event::ExitedJuror { juror: who }); Ok(()) } @@ -373,14 +435,14 @@ mod pallet { ); let vote = Vote::Secret { secret: secret_vote }; - RequestedVotes::::insert(market_id, who, vote); + RequestedVotes::::insert(&market_id, &who, vote); Self::deposit_event(Event::JurorVoted { juror: who, market_id, secret: secret_vote }); Ok(()) } // TODO benchmark - #[pallet::weight(T::WeightInfo::vote())] + #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn reveal_vote( origin: OriginFor, @@ -415,7 +477,7 @@ mod pallet { // with knowing market_id and appeal number // so even if the salt is forgotten it can be reproduced only by the juror ensure!( - secret == T::Hashing::hash_of(&(who, outcome, salt)), + secret == T::Hashing::hash_of(&(who.clone(), outcome.clone(), salt)), Error::::InvalidReveal ); secret @@ -423,8 +485,8 @@ mod pallet { _ => return Err(Error::::VoteIsNotSecret.into()), }; - let raw_vote = Vote::Revealed { secret, outcome, salt }; - RequestedVotes::::insert(market_id, who, raw_vote); + let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; + RequestedVotes::::insert(&market_id, &who, raw_vote); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); Ok(()) @@ -451,8 +513,7 @@ mod pallet { let mut count = 0u32; // TODO: use iter_from https://paritytech.github.io/substrate/master/frame_support/pallet_prelude/struct.StorageMap.html#method.iter_from // TODO: with iter_from we can iterate from the last checked item (weight restrictions) - for (outcome, crowdfund_amount) in T::Crowdfund::iter_items(court.crowdfund_info.index) - { + for (_, crowdfund_amount) in T::Crowdfund::iter_items(court.crowdfund_info.index) { if crowdfund_amount >= threshold { count = count.saturating_add(1); if count >= 2 { @@ -519,6 +580,7 @@ mod pallet { }; // sets periods one after the other from now court.update_periods(periods, now); + let appeal_number = court.appeal_info.current; court.appeal_info.current = court.appeal_info.current.saturating_add(1); let _ids_len_1 = @@ -526,10 +588,7 @@ mod pallet { >::insert(market_id, court); - Self::deposit_event(Event::MarketAppealed { - market_id, - appeal_number: court.appeal_info.current, - }); + Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); Ok(()) } @@ -578,10 +637,10 @@ mod pallet { // // If `len` is greater than the length of `jurors`, then `len` will be capped. pub(crate) fn random_jurors<'a, 'b, R>( - jurors: &'a [(T::AccountId, Juror)], + jurors: &'a [(T::AccountId, JurorInfoOf)], len: usize, rng: &mut R, - ) -> ArrayVec<&'b (T::AccountId, Juror), MAX_RANDOM_JURORS> + ) -> ArrayVec<&'b (T::AccountId, JurorInfoOf), MAX_RANDOM_JURORS> where 'a: 'b, R: RngCore, @@ -612,134 +671,16 @@ mod pallet { StdRng::from_seed(seed) } - // Used to avoid code duplications. - pub(crate) fn set_stored_juror_as_tardy(account_id: &T::AccountId) -> DispatchResult { - Self::mutate_juror(account_id, |juror| { - juror.status = JurorStatus::Tardy; - Ok(()) - }) - } - #[inline] pub(crate) fn treasury_account_id() -> T::AccountId { T::TreasuryPalletId::get().into_account_truncating() } - // 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 { + fn juror(account_id: &T::AccountId) -> Result, DispatchError> { Jurors::::get(account_id).ok_or_else(|| Error::::JurorDoesNotExists.into()) } - // # Manages tardy jurors and returns valid winners and valid losers. - // - // ## Management - // - // * Jurors that didn't vote within `CourtCaseDuration` or didn't vote at all are - // placed as tardy. - // - // * Slashes 20% of staked funds and removes tardy jurors that didn't vote or voted - // after the maximum allowed block. - // - // ## Returned list of accounts - // - // All new and old tardy jurors, excluding the ones that voted within `CourtCaseDuration`, - // are removed from the list of accounts that will be slashed to reward winners. Already - // tardy jurors that voted again on the second most voted outcome are also removed from the - // same list. - // - // In other words, does not yield slashed accounts, winners of the losing side, - // accounts that didn't vote or accounts that voted after the maximum allowed block - fn manage_tardy_jurors<'a, 'b, F>( - requested_jurors: &'a [( - T::AccountId, - Juror, - T::BlockNumber, - Option<&(T::BlockNumber, OutcomeReport)>, - )], - mut cb: F, - ) -> Result, DispatchError> - where - F: FnMut(&OutcomeReport) -> bool, - 'a: 'b, - { - let mut valid_winners_and_losers = Vec::with_capacity(requested_jurors.len()); - let treasury_account_id = Self::treasury_account_id(); - - let slash_and_remove_juror = |ai: &T::AccountId| { - let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), ai); - // Unsigned division will never overflow - let slash = all_reserved - .checked_div(&BalanceOf::::from(TARDY_PUNISHMENT_DIVISOR)) - .ok_or(DispatchError::Other("Zero division"))?; - let _ = CurrencyOf::::repatriate_reserved_named( - &Self::reserve_id(), - ai, - &treasury_account_id, - slash, - BalanceStatus::Free, - )?; - Self::remove_juror_from_all_courts_of_all_markets(ai); - Ok::<_, DispatchError>(()) - }; - - for (ai, juror, max_block, vote_opt) in requested_jurors { - if let Some((block, outcome)) = vote_opt { - let vote_is_expired = block > max_block; - if vote_is_expired { - // Tardy juror voted after maximum allowed block. Slash - if let JurorStatus::Tardy = juror.status { - slash_and_remove_juror(ai)?; - } - // Ordinary juror voted after maximum allowed block. Set as tardy - else { - Self::set_stored_juror_as_tardy(ai)?; - } - } else { - let has_voted_on_the_second_most_outcome = cb(outcome); - if has_voted_on_the_second_most_outcome { - // Don't set already tardy juror as tardy again - if JurorStatus::Tardy != juror.status { - Self::set_stored_juror_as_tardy(ai)?; - } - } else { - valid_winners_and_losers.push((ai, outcome)); - } - } - // Tardy juror didn't vote. Slash - } else if let JurorStatus::Tardy = juror.status { - slash_and_remove_juror(ai)?; - } - // Ordinary juror didn't vote. Set as tardy - else { - Self::set_stored_juror_as_tardy(ai)?; - } - } - - Ok(valid_winners_and_losers) - } - - // Modifies a stored juror. - fn mutate_juror(account_id: &T::AccountId, mut cb: F) -> DispatchResult - where - F: FnMut(&mut Juror) -> DispatchResult, - { - Jurors::::try_mutate(account_id, |opt| { - if let Some(el) = opt { - cb(el)?; - } else { - return Err(Error::::JurorDoesNotExists.into()); - } - Ok(()) - }) - } - // Calculates the necessary number of jurors depending on the number of market disputes. // // Result is capped to `usize::MAX` or in other words, capped to a very, very, very @@ -757,17 +698,19 @@ mod pallet { let mut total_incentives = BalanceOf::::from(0u8); let mut total_winners = BalanceOf::::from(0u8); - for (jai, outcome) in valid_winners_and_losers { + let mut winners = Vec::with_capacity(valid_winners_and_losers.len()); + for (juror, outcome) in valid_winners_and_losers { if outcome == &winner_outcome { + winners.push(juror); total_winners = total_winners.saturating_add(BalanceOf::::from(1u8)); } else { let all_reserved = - CurrencyOf::::reserved_balance_named(&Self::reserve_id(), jai); + CurrencyOf::::reserved_balance_named(&Self::reserve_id(), juror); // Unsigned division will never overflow let slash = all_reserved .checked_div(&BalanceOf::::from(2u8)) .ok_or(DispatchError::Other("Zero division"))?; - CurrencyOf::::slash_reserved_named(&Self::reserve_id(), jai, slash); + CurrencyOf::::slash_reserved_named(&Self::reserve_id(), juror, slash); total_incentives = total_incentives.saturating_add(slash); } } @@ -780,26 +723,55 @@ mod pallet { return Ok(()); }; - for (jai, outcome) in valid_winners_and_losers { - if outcome == &winner_outcome { - CurrencyOf::::deposit_into_existing(jai, individual_winner_incentive)?; - } + for juror in winners { + CurrencyOf::::deposit_into_existing(juror, individual_winner_incentive)?; } Ok(()) } - // For market resolution based on the votes of a market - fn two_best_outcomes( - votes: &[(T::AccountId, (T::BlockNumber, OutcomeReport))], - ) -> Result<(OutcomeReport, Option), DispatchError> { + fn aggregate( + votes: &[(T::AccountId, Vote)], + ) -> Result<(OutcomeReport, Vec<(&T::AccountId, &OutcomeReport)>), DispatchError> { let mut scores = BTreeMap::::new(); - for (_, (_, outcome_report)) in votes { - if let Some(el) = scores.get_mut(outcome_report) { - *el = el.saturating_add(1); - } else { - scores.insert(outcome_report.clone(), 1); + let treasury_account_id = Self::treasury_account_id(); + + let slash_and_remove_juror = |ai: &T::AccountId| { + let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), ai); + // Unsigned division will never overflow + let slash = all_reserved + .checked_div(&BalanceOf::::from(TARDY_PUNISHMENT_DIVISOR)) + .ok_or(DispatchError::Other("Zero division"))?; + let _ = CurrencyOf::::repatriate_reserved_named( + &Self::reserve_id(), + ai, + &treasury_account_id, + slash, + BalanceStatus::Free, + )?; + Self::remove_juror_from_all_courts_of_all_markets(ai); + Ok::<_, DispatchError>(()) + }; + + let mut valid_winners_and_losers = Vec::with_capacity(votes.len()); + + for (juror, vote) in votes { + match vote { + Vote::Drawn => { + slash_and_remove_juror(juror)?; + } + Vote::Secret { secret: _ } => { + slash_and_remove_juror(juror)?; + } + Vote::Revealed { secret: _, outcome, salt: _ } => { + if let Some(el) = scores.get_mut(&outcome) { + *el = el.saturating_add(1); + } else { + scores.insert(outcome.clone(), 1); + } + valid_winners_and_losers.push((juror, outcome)); + } } } @@ -808,30 +780,20 @@ mod pallet { let mut best_score = if let Some(first) = iter.next() { first } else { + // TODO this should never happen, we should have another vote round for it + // TODO: the appeal round should be repeated + // TODO: right after each aggregation period `on_initialize` should check if we have a clear winner (one outcome with plurality of votes) and at least one revealed vote + // TODO: if there is no clear winner, the appeal should be repeated (same appeal number) return Err(Error::::NoVotes.into()); }; - let mut second_best_score = if let Some(second) = iter.next() { - if second.1 > best_score.1 { - best_score = second; - best_score - } else { - second - } - } else { - return Ok((best_score.0.clone(), None)); - }; - for el in iter { if el.1 > best_score.1 { best_score = el; - second_best_score = best_score; - } else if el.1 > second_best_score.1 { - second_best_score = el; } } - Ok((best_score.0.clone(), Some(second_best_score.0.clone()))) + Ok((best_score.0.clone(), valid_winners_and_losers)) } // Obliterates all stored references of a juror un-reserving balances. @@ -894,10 +856,6 @@ mod pallet { Ok(()) } - // Set jurors that sided on the second most voted outcome as tardy. Jurors are only - // rewarded if sided on the most voted outcome but jurors that voted second most - // voted outcome (winner of the losing majority) are placed as tardy instead of - // being slashed. fn get_resolution_outcome( market_id: &Self::MarketId, market: &MarketOf, @@ -907,13 +865,8 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); let votes: Vec<_> = RequestedVotes::::iter_prefix(market_id).collect(); - let (first, second_opt) = Self::two_best_outcomes(votes.as_slice())?; - let valid_winners_and_losers = if let Some(second) = second_opt { - Self::manage_tardy_jurors(&requested_jurors, |outcome| outcome == &second)? - } else { - Self::manage_tardy_jurors(&requested_jurors, |_| false)? - }; - Self::slash_losers_to_award_winners(&valid_winners_and_losers, &first)?; + let (winner_outcome, valid_winners_and_losers) = Self::aggregate(votes.as_slice())?; + Self::slash_losers_to_award_winners(&valid_winners_and_losers, &winner_outcome)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -933,7 +886,7 @@ mod pallet { // this only removes a maximum of jurors according to MaxAppeals (weight limited) let _ = RequestedVotes::::clear_prefix(market_id, u32::max_value(), None); - Ok(Some(first)) + Ok(Some(winner_outcome)) } fn maybe_pay( diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index c66eb7cfe..650456e1c 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -17,7 +17,6 @@ use zeitgeist_primitives::types::OutcomeReport; -// Structure currently has only one field but acts as a container for possible future additions. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -28,23 +27,8 @@ use zeitgeist_primitives::types::OutcomeReport; PartialEq, Eq, )] -pub struct Juror { - pub(crate) status: JurorStatus, -} - -#[derive( - parity_scale_codec::Decode, - parity_scale_codec::Encode, - parity_scale_codec::MaxEncodedLen, - scale_info::TypeInfo, - Clone, - Debug, - PartialEq, - Eq, -)] -pub enum JurorStatus { - Ok, - Tardy, +pub struct JurorInfo { + pub(crate) stake: Balance, } #[derive( From e750eeae3bbf61cb201afb93d8fdb010e2f0daf2 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 20 Feb 2023 12:22:17 +0100 Subject: [PATCH 091/368] replace RequestedJurors with Elections BoundedVec --- zrml/court/src/lib.rs | 217 ++++++++++++++++++++++++---------------- zrml/court/src/types.rs | 2 +- 2 files changed, 131 insertions(+), 88 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 19efd4fd0..45f562177 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -99,16 +99,12 @@ mod pallet { weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CrowdfundInfo, CrowdfundPalletApi, JurorInfo, Periods, Vote, }; - use alloc::{ - collections::{BTreeMap, BTreeSet}, - vec::Vec, - }; - use arrayvec::ArrayVec; + use alloc::{collections::BTreeMap, vec::Vec}; use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, ensure, log, - pallet_prelude::{OptionQuery, StorageDoubleMap, StorageMap, StorageValue, ValueQuery}, + pallet_prelude::{OptionQuery, StorageMap, StorageValue, ValueQuery}, traits::{ BalanceStatus, Currency, Get, IsType, NamedReservableCurrency, Randomness, StorageVersion, @@ -119,7 +115,7 @@ mod pallet { use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng}; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating}, - DispatchError, Percent, + DispatchError, Percent, SaturatedConversion, }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, @@ -170,6 +166,10 @@ mod pallet { #[pallet::constant] type MaxAppeals: Get; + /// The maximum number of random selected jurors for a dispute. + #[pallet::constant] + type MaxElections: Get; + #[pallet::constant] type MaxJurors: Get; @@ -197,7 +197,6 @@ mod pallet { // Number of jurors for an initial market dispute const INITIAL_JURORS_NUM: usize = 3; - const MAX_RANDOM_JURORS: usize = 13; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); // Weight used to increase the number of jurors for subsequent disputes @@ -229,6 +228,10 @@ mod pallet { (BalanceOf, ::AccountId), ::MaxJurors, >; + pub(crate) type ElectionsOf = BoundedVec< + (::AccountId, Vote<::Hash>), + ::MaxElections, + >; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -247,14 +250,8 @@ mod pallet { pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; #[pallet::storage] - pub type RequestedVotes = StorageDoubleMap< - _, - Blake2_128Concat, - MarketIdOf, - Blake2_128Concat, - T::AccountId, - Vote, - >; + pub type Elections = + StorageMap<_, Blake2_128Concat, MarketIdOf, ElectionsOf, ValueQuery>; #[pallet::storage] pub type Courts = @@ -295,8 +292,6 @@ mod pallet { MarketDoesNotHaveCourtMechanism, /// No-one voted on an outcome to resolve a market NoVotes, - /// Forbids voting of unknown accounts - OnlyJurorsCanVote, /// The market is not in a state where it can be disputed. MarketIsNotDisputed, /// Only jurors can reveal their votes. @@ -332,6 +327,8 @@ mod pallet { OnlyDrawnJurorsCanVote, BelowMinStake, MaxJurorsReached, + BelowWeakestJuror, + JurorRequestedToVote, } #[pallet::call] @@ -346,6 +343,16 @@ mod pallet { let mut juror_info = JurorInfoOf:: { stake: amount }; + if jurors.is_full() { + if let Some((weakest_balance, weakest_juror)) = jurors.first() { + ensure!(amount > *weakest_balance, Error::::BelowWeakestJuror); + // TODO problem here: weakest juror could have already been requested + CurrencyOf::::unreserve_all_named(&Self::reserve_id(), &weakest_juror); + >::remove(weakest_juror); + jurors.remove(0); + } + } + if let Some(prev_juror_info) = >::get(&who) { if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { @@ -391,22 +398,29 @@ mod pallet { #[transactional] pub fn exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::juror(&who)?; + + let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExists)?; let mut jurors = JurorList::::get(); - if let Some(prev_juror_info) = >::get(&who) { - if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) - { - jurors.remove(i); - >::put(jurors); - } else { - log::warn!("Exit court: Juror stake not found in list"); - debug_assert!(false); - } + if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { + jurors.remove(i); + >::put(jurors); + } else { + log::warn!("Exit court: Juror stake not found in list"); + debug_assert!(false); } - Self::remove_juror_from_all_courts_of_all_markets(&who); + for (_, elections) in >::iter() { + ensure!( + !elections.iter().any(|(juror, _)| juror == &who), + Error::::JurorRequestedToVote + ); + } + + Jurors::::remove(&who); + + CurrencyOf::::unreserve_all_named(&Self::reserve_id(), &who); Self::deposit_event(Event::ExitedJuror { juror: who }); Ok(()) @@ -421,12 +435,6 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(>::contains_key(&who), Error::::OnlyJurorsCanVote); - ensure!( - >::contains_key(&market_id, &who), - Error::::OnlyDrawnJurorsCanVote - ); - let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( @@ -434,8 +442,16 @@ mod pallet { Error::::NotInVotingPeriod ); - let vote = Vote::Secret { secret: secret_vote }; - RequestedVotes::::insert(&market_id, &who, vote); + let mut elections = >::get(&market_id); + match elections.iter().position(|(juror, _)| juror == &who) { + Some(index) => { + let vote = Vote::Secret { secret: secret_vote }; + elections[index] = (who.clone(), vote); + } + None => return Err(Error::::OnlyDrawnJurorsCanVote.into()), + } + + >::insert(&market_id, elections); Self::deposit_event(Event::JurorVoted { juror: who, market_id, secret: secret_vote }); Ok(()) @@ -454,7 +470,6 @@ mod pallet { if Jurors::::get(&who).is_none() { return Err(Error::::OnlyJurorsCanReveal.into()); } - let vote = >::get(market_id, &who).ok_or(Error::::NoVoteFound)?; let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( @@ -469,6 +484,12 @@ mod pallet { Error::::OutcomeCrowdfundsBelowThreshold ); + let mut elections = >::get(&market_id); + let (index, vote) = match elections.iter().position(|(juror, _)| juror == &who) { + Some(index) => (index, elections[index].1.clone()), + None => return Err(Error::::NoVoteFound.into()), + }; + let secret = match vote { Vote::Secret { secret } => { // market id and current appeal number is part of salt generation @@ -486,7 +507,8 @@ mod pallet { }; let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; - RequestedVotes::::insert(&market_id, &who, raw_vote); + elections[index] = (who.clone(), raw_vote); + >::insert(&market_id, elections); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); Ok(()) @@ -599,13 +621,51 @@ mod pallet { T: Config, { pub(crate) fn select_jurors(market_id: &MarketIdOf, appeal_number: usize) { - let jurors: Vec<_> = Jurors::::iter().collect(); + let jurors: JurorListOf = JurorList::::get(); let necessary_jurors_num = Self::necessary_jurors_num(appeal_number); let mut rng = Self::rng(); - let random_jurors = Self::random_jurors(&jurors, necessary_jurors_num, &mut rng); - for (ai, _) in random_jurors { - RequestedVotes::::insert(market_id, ai, Vote::Drawn); + let actual_len = jurors.len().min(necessary_jurors_num); + + let random_jurors = jurors + .choose_multiple_weighted(&mut rng, actual_len, |item| { + let stake = item.0.saturated_into::(); + // split the u128 bits into two u64 bits and convert them to f64 + // f64 representation is only used to get weighted random selection + let lo = (stake & 0xFFFFFFFFFFFFFFFF) as u64; + let hi = (stake >> 64) as u64; + + let lo_f64 = f64::from_bits(lo); + let hi_f64 = f64::from_bits(hi); + + hi_f64 * (2.0f64).powi(64) + lo_f64 + }) + .unwrap_or_else(|err| { + log::warn!( + "Court: weighted selection failed, falling back to random selection for \ + market {:?} with error: {:?}.", + market_id, + err + ); + debug_assert!(false); + // fallback to random selection if weighted selection fails + jurors.choose_multiple(&mut rng, actual_len) + }) + .collect::>(); + + let mut elections = >::get(market_id); + for (_, ai) in random_jurors { + let res = elections.try_push((ai.clone(), Vote::Drawn)); + if let Err(err) = res { + log::warn!( + "Court: failed to add random juror {:?} to market {:?} with error: {:?}.", + ai, + market_id, + err + ); + debug_assert!(false); + } } + >::insert(market_id, elections); } pub(crate) fn check_appealable_market( @@ -633,22 +693,6 @@ mod pallet { Ok(()) } - // Returns an unique random subset of `jurors` with length `len`. - // - // If `len` is greater than the length of `jurors`, then `len` will be capped. - pub(crate) fn random_jurors<'a, 'b, R>( - jurors: &'a [(T::AccountId, JurorInfoOf)], - len: usize, - rng: &mut R, - ) -> ArrayVec<&'b (T::AccountId, JurorInfoOf), MAX_RANDOM_JURORS> - where - 'a: 'b, - R: RngCore, - { - let actual_len = jurors.len().min(len); - jurors.choose_multiple(rng, actual_len).collect() - } - /// The reserve ID of the court pallet. #[inline] pub fn reserve_id() -> [u8; 8] { @@ -676,18 +720,19 @@ mod pallet { T::TreasuryPalletId::get().into_account_truncating() } - // Retrieves a juror from the storage - fn juror(account_id: &T::AccountId) -> Result, DispatchError> { - Jurors::::get(account_id).ok_or_else(|| Error::::JurorDoesNotExists.into()) - } - // Calculates the necessary number of jurors depending on the number of market disputes. // // Result is capped to `usize::MAX` or in other words, capped to a very, very, very // high number of jurors. fn necessary_jurors_num(appeals_len: usize) -> usize { - // TODO: increase requested juror number higher; current max example: 3 + 2 * MaxAppeals - INITIAL_JURORS_NUM.saturating_add(SUBSEQUENT_JURORS_FACTOR.saturating_mul(appeals_len)) + // 2^(appeals_len) * 3 + 2^(appeals_len) - 1 + // MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 + SUBSEQUENT_JURORS_FACTOR + .saturating_pow(appeals_len as u32) + .saturating_mul(INITIAL_JURORS_NUM) + .saturating_add( + SUBSEQUENT_JURORS_FACTOR.saturating_pow(appeals_len as u32).saturating_sub(1), + ) } // Every juror that not voted on the first or second most voted outcome are slashed. @@ -750,7 +795,21 @@ mod pallet { slash, BalanceStatus::Free, )?; - Self::remove_juror_from_all_courts_of_all_markets(ai); + CurrencyOf::::unreserve_all_named(&Self::reserve_id(), ai); + let mut jurors = JurorList::::get(); + if let Some(prev_juror_info) = >::get(ai) { + if let Ok(i) = + jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + { + jurors.remove(i); + >::put(jurors); + } else { + log::warn!("Exit court: Juror stake not found in list"); + debug_assert!(false); + } + Jurors::::remove(ai); + } + Ok::<_, DispatchError>(()) }; @@ -795,19 +854,6 @@ mod pallet { Ok((best_score.0.clone(), valid_winners_and_losers)) } - - // Obliterates all stored references of a juror un-reserving balances. - fn remove_juror_from_all_courts_of_all_markets(ai: &T::AccountId) { - CurrencyOf::::unreserve_all_named(&Self::reserve_id(), ai); - Jurors::::remove(ai); - let mut market_ids = BTreeSet::new(); - market_ids.extend(RequestedVotes::::iter().map(|el| el.0)); - for market_id in &market_ids { - // TODO: should we allow removing all vote requests? - // TODO: because the juror could avoid getting slashed by just exit_court - RequestedVotes::::remove(market_id, ai); - } - } } impl DisputeApi for Pallet @@ -864,8 +910,8 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - let votes: Vec<_> = RequestedVotes::::iter_prefix(market_id).collect(); - let (winner_outcome, valid_winners_and_losers) = Self::aggregate(votes.as_slice())?; + let elections: Vec<_> = Elections::::get(market_id).into_inner(); + let (winner_outcome, valid_winners_and_losers) = Self::aggregate(elections.as_slice())?; Self::slash_losers_to_award_winners(&valid_winners_and_losers, &winner_outcome)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -882,9 +928,7 @@ mod pallet { )?; } T::Crowdfund::close_crowdfund(court.crowdfund_info.index)?; - - // this only removes a maximum of jurors according to MaxAppeals (weight limited) - let _ = RequestedVotes::::clear_prefix(market_id, u32::max_value(), None); + >::remove(market_id); Ok(Some(winner_outcome)) } @@ -942,8 +986,7 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - // only removes a maximum of jurors according to MaxAppeals (weight limited) - let _ = RequestedVotes::::clear_prefix(market_id, u32::max_value(), None); + >::remove(market_id); >::remove(market_id); Ok(()) } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 650456e1c..d4ca5a2f4 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -119,7 +119,7 @@ pub struct CourtInfo { pub(crate) periods: Periods, } -impl +impl CourtInfo { pub fn new( From 8e5efd336fec07be7f99796b4c09eff6a44d08fc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 20 Feb 2023 13:47:30 +0100 Subject: [PATCH 092/368] force slashed jurors to exit --- zrml/court/src/lib.rs | 141 +++++++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 62 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 45f562177..1dcdf4803 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -114,7 +114,7 @@ mod pallet { use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng}; use sp_runtime::{ - traits::{AccountIdConversion, CheckedDiv, Hash, Saturating}, + traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup}, DispatchError, Percent, SaturatedConversion, }; use zeitgeist_primitives::{ @@ -168,7 +168,7 @@ mod pallet { /// The maximum number of random selected jurors for a dispute. #[pallet::constant] - type MaxElections: Get; + type MaxDrawings: Get; #[pallet::constant] type MaxJurors: Get; @@ -222,15 +222,17 @@ mod pallet { MomentOf, Asset>, >; + pub(crate) type AccountIdLookupOf = + <::Lookup as StaticLookup>::Source; pub(crate) type CourtOf = CourtInfo, ::BlockNumber>; pub(crate) type JurorInfoOf = JurorInfo>; - pub(crate) type JurorListOf = BoundedVec< + pub(crate) type JurorPoolOf = BoundedVec< (BalanceOf, ::AccountId), ::MaxJurors, >; - pub(crate) type ElectionsOf = BoundedVec< + pub(crate) type DrawingsOf = BoundedVec< (::AccountId, Vote<::Hash>), - ::MaxElections, + ::MaxDrawings, >; #[pallet::pallet] @@ -239,7 +241,7 @@ mod pallet { /// Accounts that stake funds to decide outcomes. #[pallet::storage] - pub type JurorList = StorageValue<_, JurorListOf, ValueQuery>; + pub type JurorPool = StorageValue<_, JurorPoolOf, ValueQuery>; #[pallet::storage] pub type Jurors = @@ -250,8 +252,8 @@ mod pallet { pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; #[pallet::storage] - pub type Elections = - StorageMap<_, Blake2_128Concat, MarketIdOf, ElectionsOf, ValueQuery>; + pub type Drawings = + StorageMap<_, Blake2_128Concat, MarketIdOf, DrawingsOf, ValueQuery>; #[pallet::storage] pub type Courts = @@ -265,6 +267,8 @@ mod pallet { { /// A juror has been added to the court. JoinedJuror { juror: T::AccountId }, + /// A juror prepared to exit the court. + JurorPreparedExit { juror: T::AccountId }, /// A juror has been removed from the court. ExitedJuror { juror: T::AccountId }, /// A juror has voted in a court. @@ -327,8 +331,9 @@ mod pallet { OnlyDrawnJurorsCanVote, BelowMinStake, MaxJurorsReached, - BelowWeakestJuror, - JurorRequestedToVote, + JurorStillDrawn, + JurorNotPreparedToExit, + JurorNeedsToExit, } #[pallet::call] @@ -339,27 +344,17 @@ mod pallet { let who = ensure_signed(origin)?; ensure!(amount >= T::MinStake::get(), Error::::BelowMinStake); - let mut jurors = JurorList::::get(); + let mut jurors = JurorPool::::get(); let mut juror_info = JurorInfoOf:: { stake: amount }; - if jurors.is_full() { - if let Some((weakest_balance, weakest_juror)) = jurors.first() { - ensure!(amount > *weakest_balance, Error::::BelowWeakestJuror); - // TODO problem here: weakest juror could have already been requested - CurrencyOf::::unreserve_all_named(&Self::reserve_id(), &weakest_juror); - >::remove(weakest_juror); - jurors.remove(0); - } - } - if let Some(prev_juror_info) = >::get(&who) { if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { jurors.remove(i); } else { - log::warn!("Join court: Juror stake not found in list"); - debug_assert!(false); + // this happens if the juror was slashed by the vote aggregation + return Err(Error::::JurorNeedsToExit.into()); } let updated_stake = prev_juror_info.stake.saturating_add(amount); @@ -386,7 +381,7 @@ mod pallet { CurrencyOf::::reserve_named(&Self::reserve_id(), &who, amount)?; - JurorList::::put(jurors); + JurorPool::::put(jurors); >::insert(&who, juror_info); @@ -394,35 +389,55 @@ mod pallet { Ok(()) } + // TODO: benchmark #[pallet::weight(T::WeightInfo::exit_court())] #[transactional] - pub fn exit_court(origin: OriginFor) -> DispatchResult { + pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExists)?; - let mut jurors = JurorList::::get(); + let mut jurors = JurorPool::::get(); if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { + // remove from juror list to prevent being drawn jurors.remove(i); - >::put(jurors); + >::put(jurors); } else { - log::warn!("Exit court: Juror stake not found in list"); - debug_assert!(false); + // this happens if the juror was slashed by the vote aggregation + return Err(Error::::JurorNeedsToExit.into()); } - for (_, elections) in >::iter() { - ensure!( - !elections.iter().any(|(juror, _)| juror == &who), - Error::::JurorRequestedToVote - ); + Self::deposit_event(Event::JurorPreparedExit { juror: who }); + Ok(()) + } + + #[pallet::weight(T::WeightInfo::exit_court())] + #[transactional] + pub fn exit_court(origin: OriginFor, juror: AccountIdLookupOf) -> DispatchResult { + ensure_signed(origin)?; + + let juror = T::Lookup::lookup(juror)?; + + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + + ensure!( + JurorPool::::get() + .binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + .is_err(), + Error::::JurorNotPreparedToExit + ); + + // ensure not drawn for any market + for (_, drawings) in >::iter() { + ensure!(!drawings.iter().any(|(j, _)| j == &juror), Error::::JurorStillDrawn); } - Jurors::::remove(&who); + Jurors::::remove(&juror); - CurrencyOf::::unreserve_all_named(&Self::reserve_id(), &who); + CurrencyOf::::unreserve_all_named(&Self::reserve_id(), &juror); - Self::deposit_event(Event::ExitedJuror { juror: who }); + Self::deposit_event(Event::ExitedJuror { juror }); Ok(()) } @@ -442,16 +457,16 @@ mod pallet { Error::::NotInVotingPeriod ); - let mut elections = >::get(&market_id); - match elections.iter().position(|(juror, _)| juror == &who) { + let mut drawings = >::get(&market_id); + match drawings.iter().position(|(juror, _)| juror == &who) { Some(index) => { let vote = Vote::Secret { secret: secret_vote }; - elections[index] = (who.clone(), vote); + drawings[index] = (who.clone(), vote); } None => return Err(Error::::OnlyDrawnJurorsCanVote.into()), } - >::insert(&market_id, elections); + >::insert(&market_id, drawings); Self::deposit_event(Event::JurorVoted { juror: who, market_id, secret: secret_vote }); Ok(()) @@ -484,9 +499,9 @@ mod pallet { Error::::OutcomeCrowdfundsBelowThreshold ); - let mut elections = >::get(&market_id); - let (index, vote) = match elections.iter().position(|(juror, _)| juror == &who) { - Some(index) => (index, elections[index].1.clone()), + let mut drawings = >::get(&market_id); + let (index, vote) = match drawings.iter().position(|(juror, _)| juror == &who) { + Some(index) => (index, drawings[index].1.clone()), None => return Err(Error::::NoVoteFound.into()), }; @@ -507,8 +522,8 @@ mod pallet { }; let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; - elections[index] = (who.clone(), raw_vote); - >::insert(&market_id, elections); + drawings[index] = (who.clone(), raw_vote); + >::insert(&market_id, drawings); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); Ok(()) @@ -621,10 +636,11 @@ mod pallet { T: Config, { pub(crate) fn select_jurors(market_id: &MarketIdOf, appeal_number: usize) { - let jurors: JurorListOf = JurorList::::get(); + let jurors: JurorPoolOf = JurorPool::::get(); let necessary_jurors_num = Self::necessary_jurors_num(appeal_number); let mut rng = Self::rng(); - let actual_len = jurors.len().min(necessary_jurors_num); + let actual_len = + jurors.len().min(necessary_jurors_num).min(T::MaxDrawings::get() as usize); let random_jurors = jurors .choose_multiple_weighted(&mut rng, actual_len, |item| { @@ -652,9 +668,10 @@ mod pallet { }) .collect::>(); - let mut elections = >::get(market_id); + let mut drawings = >::get(market_id); for (_, ai) in random_jurors { - let res = elections.try_push((ai.clone(), Vote::Drawn)); + // below or equal MaxDrawings is ensured above + let res = drawings.try_push((ai.clone(), Vote::Drawn)); if let Err(err) = res { log::warn!( "Court: failed to add random juror {:?} to market {:?} with error: {:?}.", @@ -665,7 +682,7 @@ mod pallet { debug_assert!(false); } } - >::insert(market_id, elections); + >::insert(market_id, drawings); } pub(crate) fn check_appealable_market( @@ -795,19 +812,19 @@ mod pallet { slash, BalanceStatus::Free, )?; - CurrencyOf::::unreserve_all_named(&Self::reserve_id(), ai); - let mut jurors = JurorList::::get(); + if let Some(prev_juror_info) = >::get(ai) { + let mut jurors = JurorPool::::get(); if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { + // remove from juror list to prevent being drawn jurors.remove(i); - >::put(jurors); - } else { - log::warn!("Exit court: Juror stake not found in list"); - debug_assert!(false); + >::put(jurors); } - Jurors::::remove(ai); + } else { + log::warn!("Juror {:?} not found in Jurors storage for vote aggregation.", ai); + debug_assert!(false); } Ok::<_, DispatchError>(()) @@ -910,8 +927,8 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - let elections: Vec<_> = Elections::::get(market_id).into_inner(); - let (winner_outcome, valid_winners_and_losers) = Self::aggregate(elections.as_slice())?; + let drawings: Vec<_> = Drawings::::get(market_id).into_inner(); + let (winner_outcome, valid_winners_and_losers) = Self::aggregate(drawings.as_slice())?; Self::slash_losers_to_award_winners(&valid_winners_and_losers, &winner_outcome)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -928,7 +945,7 @@ mod pallet { )?; } T::Crowdfund::close_crowdfund(court.crowdfund_info.index)?; - >::remove(market_id); + >::remove(market_id); Ok(Some(winner_outcome)) } @@ -986,7 +1003,7 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - >::remove(market_id); + >::remove(market_id); >::remove(market_id); Ok(()) } From 51b2e04ceb38651fbb368a782ddc19b7c7461414 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 20 Feb 2023 14:49:27 +0100 Subject: [PATCH 093/368] allow denouncing --- zrml/court/src/lib.rs | 139 ++++++++++++++++++++++++++++++++++------ zrml/court/src/types.rs | 1 + 2 files changed, 120 insertions(+), 20 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 1dcdf4803..e1b4dd532 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -146,6 +146,10 @@ mod pallet { #[pallet::constant] type CrowdfundMinThreshold: Get>; + /// The slash percentage if the vote gets revealed during the voting period. + #[pallet::constant] + type DenounceSlashPercentage: Get; + type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, @@ -183,6 +187,13 @@ mod pallet { /// Randomness source type Random: Randomness; + #[pallet::constant] + type RedistributionPercentage: Get; + + /// The percentage that is being slashed from the juror's stake. + #[pallet::constant] + type SlashPercentage: Get; + /// Weight used to calculate the necessary staking amount to become a juror #[pallet::constant] type StakeWeight: Get>; @@ -202,9 +213,6 @@ mod pallet { // Weight used to increase the number of jurors for subsequent disputes // of the same market const SUBSEQUENT_JURORS_FACTOR: usize = 2; - // Divides the reserved juror balance to calculate the slash amount. `5` here - // means that the output value will be 20% of the dividend. - const TARDY_PUNISHMENT_DIVISOR: u8 = 5; pub(crate) type BalanceOf = as Currency<::AccountId>>::Balance; @@ -280,6 +288,14 @@ mod pallet { outcome: OutcomeReport, salt: T::Hash, }, + /// A juror vote has been denounced. + DenouncedJurorVote { + denouncer: T::AccountId, + juror: T::AccountId, + market_id: MarketIdOf, + outcome: OutcomeReport, + salt: T::Hash, + }, /// The jurors for an appeal have been drawn. AppealJurorsDrawn { market_id: MarketIdOf }, /// The crowdfund for an appeal has been checked. @@ -300,10 +316,8 @@ mod pallet { MarketIsNotDisputed, /// Only jurors can reveal their votes. OnlyJurorsCanReveal, - /// The vote was not found. - NoVoteFound, /// The vote is not secret. - VoteIsNotSecret, + VoteAlreadyRevealed, /// The outcome and salt reveal do not match the secret vote. InvalidReveal, /// The revealed vote outcome was not crowdfunded. @@ -334,6 +348,10 @@ mod pallet { JurorStillDrawn, JurorNotPreparedToExit, JurorNeedsToExit, + JurorNotDrawn, + JurorNotVoted, + VoteAlreadyDenounced, + DenouncerCannotBeJuror, } #[pallet::call] @@ -472,6 +490,87 @@ mod pallet { Ok(()) } + // TODO benchmark + #[pallet::weight(1_000_000_000_000)] + #[transactional] + pub fn denounce_vote( + origin: OriginFor, + #[pallet::compact] market_id: MarketIdOf, + juror: AccountIdLookupOf, + outcome: OutcomeReport, + salt: T::Hash, + ) -> DispatchResult { + let denouncer = ensure_signed(origin)?; + + let juror = T::Lookup::lookup(juror)?; + + ensure!(denouncer != juror, Error::::DenouncerCannotBeJuror); + + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + + let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let now = >::block_number(); + // ensure in vote period + ensure!( + court.periods.crowdfund_end < now && now <= court.periods.vote_end, + Error::::NotInVotingPeriod + ); + + let mut drawings = >::get(&market_id); + let (index, vote) = match drawings.iter().position(|(j, _)| j == &juror) { + Some(index) => (index, drawings[index].1.clone()), + None => return Err(Error::::JurorNotDrawn.into()), + }; + + let secret = match vote { + Vote::Secret { secret } => { + ensure!( + secret == T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)), + Error::::InvalidReveal + ); + secret + } + Vote::Drawn => return Err(Error::::JurorNotVoted.into()), + Vote::Revealed { secret: _, outcome: _, salt: _ } => { + return Err(Error::::VoteAlreadyRevealed.into()); + } + Vote::Denounced { secret: _, outcome: _, salt: _ } => { + return Err(Error::::VoteAlreadyDenounced.into()); + } + }; + + let treasury_account_id = Self::treasury_account_id(); + let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), &juror); + let slash = T::DenounceSlashPercentage::get() * all_reserved; + let _ = CurrencyOf::::repatriate_reserved_named( + &Self::reserve_id(), + &juror, + &treasury_account_id, + slash, + BalanceStatus::Free, + )?; + + let mut jurors = JurorPool::::get(); + if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { + // remove from juror list to prevent being drawn + jurors.remove(i); + >::put(jurors); + } + + let raw_vote = Vote::Denounced { secret, outcome: outcome.clone(), salt }; + drawings[index] = (juror.clone(), raw_vote); + >::insert(&market_id, drawings); + + Self::deposit_event(Event::DenouncedJurorVote { + denouncer, + juror, + market_id, + outcome, + salt, + }); + Ok(()) + } + // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] @@ -482,9 +581,8 @@ mod pallet { salt: T::Hash, ) -> DispatchResult { let who = ensure_signed(origin)?; - if Jurors::::get(&who).is_none() { - return Err(Error::::OnlyJurorsCanReveal.into()); - } + + ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( @@ -502,7 +600,7 @@ mod pallet { let mut drawings = >::get(&market_id); let (index, vote) = match drawings.iter().position(|(juror, _)| juror == &who) { Some(index) => (index, drawings[index].1.clone()), - None => return Err(Error::::NoVoteFound.into()), + None => return Err(Error::::JurorNotDrawn.into()), }; let secret = match vote { @@ -518,7 +616,13 @@ mod pallet { ); secret } - _ => return Err(Error::::VoteIsNotSecret.into()), + Vote::Drawn => return Err(Error::::JurorNotVoted.into()), + Vote::Revealed { secret: _, outcome: _, salt: _ } => { + return Err(Error::::VoteAlreadyRevealed.into()); + } + Vote::Denounced { secret: _, outcome: _, salt: _ } => { + return Err(Error::::VoteAlreadyDenounced.into()); + } }; let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; @@ -752,7 +856,6 @@ mod pallet { ) } - // Every juror that not voted on the first or second most voted outcome are slashed. fn slash_losers_to_award_winners( valid_winners_and_losers: &[(&T::AccountId, &OutcomeReport)], winner_outcome: &OutcomeReport, @@ -768,10 +871,7 @@ mod pallet { } else { let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), juror); - // Unsigned division will never overflow - let slash = all_reserved - .checked_div(&BalanceOf::::from(2u8)) - .ok_or(DispatchError::Other("Zero division"))?; + let slash = T::RedistributionPercentage::get() * all_reserved; CurrencyOf::::slash_reserved_named(&Self::reserve_id(), juror, slash); total_incentives = total_incentives.saturating_add(slash); } @@ -801,10 +901,7 @@ mod pallet { let slash_and_remove_juror = |ai: &T::AccountId| { let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), ai); - // Unsigned division will never overflow - let slash = all_reserved - .checked_div(&BalanceOf::::from(TARDY_PUNISHMENT_DIVISOR)) - .ok_or(DispatchError::Other("Zero division"))?; + let slash = T::SlashPercentage::get() * all_reserved; let _ = CurrencyOf::::repatriate_reserved_named( &Self::reserve_id(), ai, @@ -840,6 +937,8 @@ mod pallet { Vote::Secret { secret: _ } => { slash_and_remove_juror(juror)?; } + // denounce extrinsic already slashed the juror + Vote::Denounced { secret: _, outcome: _, salt: _ } => (), Vote::Revealed { secret: _, outcome, salt: _ } => { if let Some(el) = scores.get_mut(&outcome) { *el = el.saturating_add(1); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index d4ca5a2f4..423b18e95 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -45,6 +45,7 @@ pub enum Vote { Drawn, Secret { secret: Hash }, Revealed { secret: Hash, outcome: OutcomeReport, salt: Hash }, + Denounced { secret: Hash, outcome: OutcomeReport, salt: Hash }, } #[derive( From 3777185018c883bc0d613c1ead794f7e57c2ab2f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 21 Feb 2023 15:06:26 +0100 Subject: [PATCH 094/368] remove crowdfunding for now --- runtime/common/src/lib.rs | 36 ----- zrml/court/src/lib.rs | 210 ++++++---------------------- zrml/court/src/mock.rs | 37 ----- zrml/court/src/types.rs | 49 ++----- zrml/prediction-markets/src/mock.rs | 37 ----- 5 files changed, 54 insertions(+), 315 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index ca017a1e0..f91764419 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -934,43 +934,7 @@ macro_rules! impl_config_traits { type WeightInfo = zrml_authorized::weights::WeightInfo; } - pub struct CrowdfundNoop; - - type CurrencyOf = ::Currency; - type NegativeImbalanceOf = as Currency< - ::AccountId, - >>::NegativeImbalance; - - impl zrml_court::CrowdfundPalletApi> - for CrowdfundNoop - { - fn open_crowdfund() -> Result { - Ok(0) - } - - fn iter_items( - _fund_index: u128, - ) -> frame_support::storage::PrefixIterator<(zeitgeist_primitives::types::OutcomeReport, Balance)> - { - unimplemented!() - } - - fn prepare_refund( - _fund_index: u128, - _item: &zeitgeist_primitives::types::OutcomeReport, - _fee: sp_runtime::Percent, - ) -> Result, frame_support::pallet_prelude::DispatchError> { - Ok(NegativeImbalanceOf::::zero()) - } - - fn close_crowdfund(_fund_index: u128) -> frame_support::pallet_prelude::DispatchResult { - Ok(()) - } - } - impl zrml_court::Config for Runtime { - type Crowdfund = CrowdfundNoop; - type CourtCaseDuration = CourtCaseDuration; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type MarketCommons = MarketCommons; diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index e1b4dd532..37dda6085 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -37,74 +37,18 @@ pub use court_pallet_api::CourtPalletApi; pub use pallet::*; pub use types::*; -// TODO: remove this crowdfund interface and use the real after crowdfund pallet is merged -use frame_support::pallet_prelude::{DispatchError, DispatchResult}; -use zeitgeist_primitives::types::OutcomeReport; - -/// The trait for handling of crowdfunds. -pub trait CrowdfundPalletApi { - /// Create a new crowdfund. - /// - /// # Returns - /// - `FundIndex` - The id of the crowdfund. - fn open_crowdfund() -> Result; - - /// Get an iterator over all items of a crowdfund. - /// - /// # Arguments - /// - `fund_index` - The id of the crowdfund. - /// - /// # Returns - /// - `PrefixIterator` - The iterator over all items of the crowdfund. - fn iter_items( - fund_index: u128, - ) -> frame_support::storage::PrefixIterator<(OutcomeReport, Balance)>; - - /// Maybe get an item of a crowdfund. - /// - /// # Arguments - /// - `fund_index` - The id of the crowdfund. - /// - `item` - The item to get. - /// - /// # Returns - /// - `Option` - The balance of the item. - fn get_item(fund_index: u128, item: &OutcomeReport) -> Option; - - /// Prepare for all related backers to potentially refund their stake. - /// - /// # Arguments - /// - `fund_index` - The id of the crowdfund. - /// - `item` - The item to refund. - /// - `fee` - The overall fee to charge from the fund item - /// before the backer refunds are possible. - /// - /// # Returns - /// - `NegativeImbalance` - The imbalance that contains the charged fees. - fn prepare_refund( - fund_index: u128, - item: &OutcomeReport, - fee: sp_runtime::Percent, - ) -> Result; - - /// Close a crowdfund. - /// - /// # Arguments - /// - `fund_index` - The id of the crowdfund. - fn close_crowdfund(fund_index: u128) -> DispatchResult; -} - #[frame_support::pallet] mod pallet { use crate::{ - weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CrowdfundInfo, CrowdfundPalletApi, - JurorInfo, Periods, Vote, + weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, JurorInfo, Periods, + Vote, }; use alloc::{collections::BTreeMap, vec::Vec}; use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, ensure, log, - pallet_prelude::{OptionQuery, StorageMap, StorageValue, ValueQuery}, + pallet_prelude::{EnsureOrigin, OptionQuery, StorageMap, StorageValue, ValueQuery}, traits::{ BalanceStatus, Currency, Get, IsType, NamedReservableCurrency, Randomness, StorageVersion, @@ -125,8 +69,10 @@ mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + type AppealOrigin: EnsureOrigin; + #[pallet::constant] - type CourtCrowdfundPeriod: Get; + type CourtBackingPeriod: Get; #[pallet::constant] type CourtVotePeriod: Get; @@ -137,12 +83,6 @@ mod pallet { #[pallet::constant] type CourtAppealPeriod: Get; - type Crowdfund: crate::CrowdfundPalletApi< - Self::AccountId, - BalanceOf, - NegativeImbalanceOf, - >; - #[pallet::constant] type CrowdfundMinThreshold: Get>; @@ -232,7 +172,7 @@ mod pallet { >; pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - pub(crate) type CourtOf = CourtInfo, ::BlockNumber>; + pub(crate) type CourtOf = CourtInfo<::BlockNumber>; pub(crate) type JurorInfoOf = JurorInfo>; pub(crate) type JurorPoolOf = BoundedVec< (BalanceOf, ::AccountId), @@ -298,8 +238,8 @@ mod pallet { }, /// The jurors for an appeal have been drawn. AppealJurorsDrawn { market_id: MarketIdOf }, - /// The crowdfund for an appeal has been checked. - AppealCrowdfundChecked { market_id: MarketIdOf }, + /// The backing for an appeal has been checked. + AppealBackingChecked { market_id: MarketIdOf }, /// A market has been appealed. MarketAppealed { market_id: MarketIdOf, appeal_number: u8 }, } @@ -320,27 +260,21 @@ mod pallet { VoteAlreadyRevealed, /// The outcome and salt reveal do not match the secret vote. InvalidReveal, - /// The revealed vote outcome was not crowdfunded. - InvalidCrowdfundItem, /// No court for this market id was found. CourtNotFound, /// This operation is only allowed in the voting period. NotInVotingPeriod, /// This operation is only allowed in the aggregation period. NotInAggregationPeriod, - /// There is not enough crowdfund backing to appeal. - NotEnoughCrowdfundBackingToAppeal, /// The maximum number of appeals has been reached. MaxAppealsReached, /// This operation is only allowed in the appeal period. NotInAppealPeriod, /// The court is already present for this market. CourtAlreadyExists, - /// The revealed outcome is below the minimum threshold for the crowdfund. - OutcomeCrowdfundsBelowThreshold, JurorsAlreadyDrawn, AppealAlreadyFunded, - CheckCrowdfundFirst, + CheckBackingFirst, AppealNotReady, OnlyDrawnJurorsCanVote, BelowMinStake, @@ -351,7 +285,8 @@ mod pallet { JurorNotDrawn, JurorNotVoted, VoteAlreadyDenounced, - DenouncerCannotBeJuror, + SelfDenounceDisallowed, + AmountAlreadyUsed, } #[pallet::call] @@ -375,28 +310,19 @@ mod pallet { return Err(Error::::JurorNeedsToExit.into()); } - let updated_stake = prev_juror_info.stake.saturating_add(amount); - match jurors.binary_search_by_key(&updated_stake, |tuple| tuple.0) { - Ok(i) => jurors - .try_insert(i, (updated_stake, who.clone())) - .map_err(|_| Error::::MaxJurorsReached)?, - Err(i) => jurors - .try_insert(i, (updated_stake, who.clone())) - .map_err(|_| Error::::MaxJurorsReached)?, - }; - - juror_info.stake = updated_stake; - } else { - match jurors.binary_search_by_key(&amount, |tuple| tuple.0) { - Ok(i) => jurors - .try_insert(i, (amount, who.clone())) - .map_err(|_| Error::::MaxJurorsReached)?, - Err(i) => jurors - .try_insert(i, (amount, who.clone())) - .map_err(|_| Error::::MaxJurorsReached)?, - }; + juror_info.stake = prev_juror_info.stake.saturating_add(amount); } + match jurors.binary_search_by_key(&juror_info.stake, |tuple| tuple.0) { + // The reason for this error is that each amount has a clear juror + // binary_search_by_key could otherwise return an index of an unwanted juror + // if there are multiple jurors with the same stake + Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), + Err(i) => jurors + .try_insert(i, (amount, who.clone())) + .map_err(|_| Error::::MaxJurorsReached)?, + }; + CurrencyOf::::reserve_named(&Self::reserve_id(), &who, amount)?; JurorPool::::put(jurors); @@ -407,7 +333,6 @@ mod pallet { Ok(()) } - // TODO: benchmark #[pallet::weight(T::WeightInfo::exit_court())] #[transactional] pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { @@ -471,7 +396,7 @@ mod pallet { let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( - court.periods.crowdfund_end < now && now <= court.periods.vote_end, + court.periods.backing_end < now && now <= court.periods.vote_end, Error::::NotInVotingPeriod ); @@ -490,7 +415,6 @@ mod pallet { Ok(()) } - // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn denounce_vote( @@ -504,7 +428,7 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; - ensure!(denouncer != juror, Error::::DenouncerCannotBeJuror); + ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; @@ -512,7 +436,7 @@ mod pallet { let now = >::block_number(); // ensure in vote period ensure!( - court.periods.crowdfund_end < now && now <= court.periods.vote_end, + court.periods.backing_end < now && now <= court.periods.vote_end, Error::::NotInVotingPeriod ); @@ -541,6 +465,7 @@ mod pallet { let treasury_account_id = Self::treasury_account_id(); let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), &juror); + // TODO: should the denouncer profit from this? let slash = T::DenounceSlashPercentage::get() * all_reserved; let _ = CurrencyOf::::repatriate_reserved_named( &Self::reserve_id(), @@ -571,7 +496,6 @@ mod pallet { Ok(()) } - // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn reveal_vote( @@ -590,13 +514,6 @@ mod pallet { Error::::NotInAggregationPeriod ); - let fund_amount = T::Crowdfund::get_item(court.crowdfund_info.index, &outcome) - .ok_or(Error::::InvalidCrowdfundItem)?; - ensure!( - fund_amount >= court.crowdfund_info.threshold, - Error::::OutcomeCrowdfundsBelowThreshold - ); - let mut drawings = >::get(&market_id); let (index, vote) = match drawings.iter().position(|(juror, _)| juror == &who) { Some(index) => (index, drawings[index].1.clone()), @@ -633,47 +550,27 @@ mod pallet { Ok(()) } - // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] - pub fn check_appeal_crowdfund( + pub fn check_appeal_backing( origin: OriginFor, market_id: MarketIdOf, ) -> DispatchResult { - ensure_signed(origin)?; + T::AppealOrigin::ensure_origin(origin)?; let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; - ensure!(!court.appeal_info.is_funded, Error::::AppealAlreadyFunded); + ensure!(!court.appeal_info.is_backed, Error::::AppealAlreadyFunded); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - // update crowdfund threshold - let threshold = - court.crowdfund_info.threshold.saturating_add(court.crowdfund_info.threshold); - - let mut count = 0u32; - // TODO: use iter_from https://paritytech.github.io/substrate/master/frame_support/pallet_prelude/struct.StorageMap.html#method.iter_from - // TODO: with iter_from we can iterate from the last checked item (weight restrictions) - for (_, crowdfund_amount) in T::Crowdfund::iter_items(court.crowdfund_info.index) { - if crowdfund_amount >= threshold { - count = count.saturating_add(1); - if count >= 2 { - break; - } - } - } - ensure!(count >= 2, Error::::NotEnoughCrowdfundBackingToAppeal); - - court.crowdfund_info.threshold = threshold; - court.appeal_info.is_funded = true; + court.appeal_info.is_backed = true; >::insert(&market_id, court); - Self::deposit_event(Event::AppealCrowdfundChecked { market_id }); + Self::deposit_event(Event::AppealBackingChecked { market_id }); Ok(()) } - // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn draw_appeal_jurors( @@ -684,7 +581,7 @@ mod pallet { let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; ensure!(!court.appeal_info.is_drawn, Error::::JurorsAlreadyDrawn); - ensure!(court.appeal_info.is_funded, Error::::CheckCrowdfundFirst); + ensure!(court.appeal_info.is_backed, Error::::CheckBackingFirst); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; @@ -699,7 +596,6 @@ mod pallet { Ok(()) } - // TODO benchmark #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { @@ -714,7 +610,7 @@ mod pallet { let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); let periods = Periods { - crowdfund_end: T::CourtCrowdfundPeriod::get(), + backing_end: T::CourtBackingPeriod::get(), vote_end: T::CourtVotePeriod::get(), aggregation_end: T::CourtAggregationPeriod::get(), appeal_end: T::CourtAppealPeriod::get(), @@ -806,10 +702,8 @@ mod pallet { Error::::NotInAppealPeriod ); - ensure!( - court.appeal_info.current < court.appeal_info.max, - Error::::MaxAppealsReached - ); + // TODO for now allow as many appeals as needed, later check if max appeals is reached + // TODO return error when max appeals is reached here and only allow global disputes to take over Ok(()) } @@ -955,10 +849,6 @@ mod pallet { let mut best_score = if let Some(first) = iter.next() { first } else { - // TODO this should never happen, we should have another vote round for it - // TODO: the appeal round should be repeated - // TODO: right after each aggregation period `on_initialize` should check if we have a clear winner (one outcome with plurality of votes) and at least one revealed vote - // TODO: if there is no clear winner, the appeal should be repeated (same appeal number) return Err(Error::::NoVotes.into()); }; @@ -993,20 +883,16 @@ mod pallet { ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); let now = >::block_number(); - let index = T::Crowdfund::open_crowdfund()?; - - let crowdfund_info = - CrowdfundInfo { index, threshold: T::CrowdfundMinThreshold::get() }; let periods = Periods { - crowdfund_end: T::CourtCrowdfundPeriod::get(), + backing_end: T::CourtBackingPeriod::get(), vote_end: T::CourtVotePeriod::get(), aggregation_end: T::CourtAggregationPeriod::get(), appeal_end: T::CourtAppealPeriod::get(), }; // sets periods one after the other from now - let court = CourtInfo::new(crowdfund_info, now, periods, T::MaxAppeals::get() as u8); + let court = CourtInfo::new(now, periods, T::MaxAppeals::get() as u8); Self::select_jurors(market_id, 0usize); @@ -1030,20 +916,6 @@ mod pallet { let (winner_outcome, valid_winners_and_losers) = Self::aggregate(drawings.as_slice())?; Self::slash_losers_to_award_winners(&valid_winners_and_losers, &winner_outcome)?; - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - - // TODO: use an own API call instead, which allows to prepare the refund for all inside the crowdfund pallet (call extrinsic multiple times) - // TODO: specify fees somehow for specific outcomes in this api call - // TODO: the reason for this is that there are weight limitations here (storage iter) - for (outcome, _crowdfund_amount) in T::Crowdfund::iter_items(court.crowdfund_info.index) - { - T::Crowdfund::prepare_refund( - court.crowdfund_info.index, - &outcome, - Percent::zero(), - )?; - } - T::Crowdfund::close_crowdfund(court.crowdfund_info.index)?; >::remove(market_id); Ok(Some(winner_outcome)) @@ -1077,7 +949,7 @@ mod pallet { } fn has_failed( - market_id: &Self::MarketId, + _market_id: &Self::MarketId, market: &MarketOf, ) -> Result { ensure!( @@ -1085,8 +957,8 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - Ok(court.appeal_info.current >= court.appeal_info.max) + // TODO: for now disallow global dispute for court, later use max appeals check + Ok(false) } fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index b8a792602..7048587ac 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -105,44 +105,7 @@ impl DisputeResolutionApi for NoopResolution { } } -pub struct CrowdfundNoop; - -use frame_support::traits::{Currency, Imbalance}; -type CurrencyOf = ::Currency; -type NegativeImbalanceOf = as Currency< - ::AccountId, ->>::NegativeImbalance; - -impl zrml_court::CrowdfundPalletApi> - for CrowdfundNoop -{ - fn open_crowdfund() -> Result { - Ok(0) - } - - fn iter_items( - _fund_index: u128, - ) -> frame_support::storage::PrefixIterator<(zeitgeist_primitives::types::OutcomeReport, Balance)> - { - unimplemented!() - } - - fn prepare_refund( - _fund_index: u128, - _item: &zeitgeist_primitives::types::OutcomeReport, - _fee: sp_runtime::Percent, - ) -> Result, frame_support::pallet_prelude::DispatchError> { - Ok(NegativeImbalanceOf::::zero()) - } - - fn close_crowdfund(_fund_index: u128) -> frame_support::pallet_prelude::DispatchResult { - Ok(()) - } -} - impl crate::Config for Runtime { - type Crowdfund = CrowdfundNoop; - type CourtCaseDuration = CourtCaseDuration; type DisputeResolution = NoopResolution; type Event = (); type MarketCommons = MarketCommons; diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 423b18e95..4870ab4ef 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -48,21 +48,6 @@ pub enum Vote { Denounced { secret: Hash, outcome: OutcomeReport, salt: Hash }, } -#[derive( - parity_scale_codec::Decode, - parity_scale_codec::Encode, - parity_scale_codec::MaxEncodedLen, - scale_info::TypeInfo, - Clone, - Debug, - PartialEq, - Eq, -)] -pub struct CrowdfundInfo { - pub(crate) index: u128, - pub(crate) threshold: Balance, -} - #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -74,7 +59,7 @@ pub struct CrowdfundInfo { Eq, )] pub struct Periods { - pub(crate) crowdfund_end: BlockNumber, + pub(crate) backing_end: BlockNumber, pub(crate) vote_end: BlockNumber, pub(crate) aggregation_end: BlockNumber, pub(crate) appeal_end: BlockNumber, @@ -94,12 +79,12 @@ pub struct AppealInfo { pub(crate) current: u8, pub(crate) max: u8, pub(crate) is_drawn: bool, - pub(crate) is_funded: bool, + pub(crate) is_backed: bool, } impl AppealInfo { pub fn is_appeal_ready(&self) -> bool { - self.is_drawn && self.is_funded + self.is_drawn && self.is_backed } } @@ -113,35 +98,27 @@ impl AppealInfo { PartialEq, Eq, )] -pub struct CourtInfo { - pub(crate) crowdfund_info: CrowdfundInfo, +pub struct CourtInfo { pub(crate) appeal_info: AppealInfo, pub(crate) winner: Option, pub(crate) periods: Periods, } -impl - CourtInfo -{ - pub fn new( - crowdfund_info: CrowdfundInfo, - now: BlockNumber, - periods: Periods, - max_appeals: u8, - ) -> Self { - let crowdfund_end = now.saturating_add(periods.crowdfund_end); - let vote_end = crowdfund_end.saturating_add(periods.vote_end); +impl CourtInfo { + pub fn new(now: BlockNumber, periods: Periods, max_appeals: u8) -> Self { + let backing_end = now.saturating_add(periods.backing_end); + let vote_end = backing_end.saturating_add(periods.vote_end); let aggregation_end = vote_end.saturating_add(periods.aggregation_end); let appeal_end = aggregation_end.saturating_add(periods.appeal_end); - let periods = Periods { crowdfund_end, vote_end, aggregation_end, appeal_end }; + let periods = Periods { backing_end, vote_end, aggregation_end, appeal_end }; let appeal_info = - AppealInfo { current: 1, max: max_appeals, is_drawn: false, is_funded: false }; - Self { crowdfund_info, appeal_info, winner: None, periods } + AppealInfo { current: 1, max: max_appeals, is_drawn: false, is_backed: false }; + Self { appeal_info, winner: None, periods } } pub fn update_periods(&mut self, periods: Periods, now: BlockNumber) { - self.periods.crowdfund_end = now.saturating_add(periods.crowdfund_end); - self.periods.vote_end = self.periods.crowdfund_end.saturating_add(periods.vote_end); + self.periods.backing_end = now.saturating_add(periods.backing_end); + self.periods.vote_end = self.periods.backing_end.saturating_add(periods.vote_end); self.periods.aggregation_end = self.periods.vote_end.saturating_add(periods.aggregation_end); self.periods.appeal_end = self.periods.aggregation_end.saturating_add(periods.appeal_end); diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index d73a90fde..f8453aca6 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -275,44 +275,7 @@ impl zrml_authorized::Config for Runtime { type WeightInfo = zrml_authorized::weights::WeightInfo; } -pub struct CrowdfundNoop; - -use frame_support::traits::{Currency, Imbalance}; -type CurrencyOf = ::Currency; -type NegativeImbalanceOf = as Currency< - ::AccountId, ->>::NegativeImbalance; - -impl zrml_court::CrowdfundPalletApi> - for CrowdfundNoop -{ - fn open_crowdfund() -> Result { - Ok(0) - } - - fn iter_items( - _fund_index: u128, - ) -> frame_support::storage::PrefixIterator<(zeitgeist_primitives::types::OutcomeReport, Balance)> - { - unimplemented!() - } - - fn prepare_refund( - _fund_index: u128, - _item: &zeitgeist_primitives::types::OutcomeReport, - _fee: sp_runtime::Percent, - ) -> Result, frame_support::pallet_prelude::DispatchError> { - Ok(NegativeImbalanceOf::::zero()) - } - - fn close_crowdfund(_fund_index: u128) -> frame_support::pallet_prelude::DispatchResult { - Ok(()) - } -} - impl zrml_court::Config for Runtime { - type CourtCaseDuration = CourtCaseDuration; - type Crowdfund = CrowdfundNoop; type DisputeResolution = prediction_markets::Pallet; type Event = Event; type MarketCommons = MarketCommons; From 55287646102fe7cc38e30242bda57807d96b53f1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 22 Feb 2023 16:52:59 +0100 Subject: [PATCH 095/368] wip --- primitives/src/constants/mock.rs | 12 + zrml/court/src/benchmarks.rs | 27 +- zrml/court/src/lib.rs | 596 ++++++++++++++++++++-------- zrml/court/src/mock.rs | 25 +- zrml/court/src/tests.rs | 215 +++------- zrml/court/src/types.rs | 21 +- zrml/prediction-markets/src/mock.rs | 32 +- 7 files changed, 560 insertions(+), 368 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 9d9853d5d..5a2250e78 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -7,6 +7,7 @@ use crate::{ }; use frame_support::{parameter_types, traits::LockIdentifier, PalletId}; use orml_traits::parameter_type_with_key; +use sp_runtime::Percent; // Authorized parameter_types! { @@ -19,6 +20,17 @@ parameter_types! { pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); pub const StakeWeight: u128 = 2 * BASE; + pub const CourtBackingPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); + pub const MaxAppeals: u32 = 5; + pub const MaxDrawings: u32 = 127; + pub const MaxJurors: u32 = 10_000; + pub const MinJurorStake: Balance = 2 * BASE; + pub const RedistributionPercentage: Percent = Percent::from_percent(50); + pub const SlashPercentage: Percent = Percent::from_percent(10); } // Global disputes parameters diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index e1b86b30c..68422b8ce 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -25,10 +25,9 @@ use crate::Pallet as Court; use crate::{BalanceOf, Call, Config, CurrencyOf, Pallet}; use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_support::{dispatch::UnfilteredDispatchable, traits::Currency}; +use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; -use zeitgeist_primitives::types::OutcomeReport; fn deposit(caller: &T::AccountId) where @@ -37,33 +36,13 @@ where let _ = CurrencyOf::::deposit_creating(caller, BalanceOf::::max_value()); } -fn deposit_and_join_court(caller: &T::AccountId) -where - T: Config, -{ - deposit::(caller); - Call::::join_court {} - .dispatch_bypass_filter(RawOrigin::Signed(caller.clone()).into()) - .unwrap(); -} - benchmarks! { - exit_court { - let caller: T::AccountId = whitelisted_caller(); - deposit_and_join_court::(&caller); - }: _(RawOrigin::Signed(caller)) - join_court { let caller: T::AccountId = whitelisted_caller(); deposit::(&caller); - }: _(RawOrigin::Signed(caller)) + let amount = T::MinJurorStake::get(); + }: _(RawOrigin::Signed(caller), amount) - vote { - let caller: T::AccountId = whitelisted_caller(); - let market_id = Default::default(); - let outcome = OutcomeReport::Scalar(u128::MAX); - deposit_and_join_court::(&caller); - }: _(RawOrigin::Signed(caller), market_id, outcome) impl_benchmark_test_suite!( Court, diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 37dda6085..9867de7d1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -40,7 +40,7 @@ pub use types::*; #[frame_support::pallet] mod pallet { use crate::{ - weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, JurorInfo, Periods, + weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CourtStatus, JurorInfo, Periods, Vote, }; use alloc::{collections::BTreeMap, vec::Vec}; @@ -50,13 +50,13 @@ mod pallet { ensure, log, pallet_prelude::{EnsureOrigin, OptionQuery, StorageMap, StorageValue, ValueQuery}, traits::{ - BalanceStatus, Currency, Get, IsType, NamedReservableCurrency, Randomness, + BalanceStatus, Currency, Get, Imbalance, IsType, NamedReservableCurrency, Randomness, StorageVersion, }, transactional, Blake2_128Concat, BoundedVec, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; - use rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng}; + use rand::{rngs::StdRng, RngCore, SeedableRng}; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup}, DispatchError, Percent, SaturatedConversion, @@ -69,27 +69,32 @@ mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The origin which may start appeals. type AppealOrigin: EnsureOrigin; + /// The time to wait before jurors can start voting. + /// The intention is to use this period as preparation time + /// (for example vote outcome addition through crowdfunding) #[pallet::constant] type CourtBackingPeriod: Get; + /// The time in which the jurors can cast their secret vote. #[pallet::constant] type CourtVotePeriod: Get; + /// The time in which the jurors should reveal their secret vote. #[pallet::constant] type CourtAggregationPeriod: Get; + /// The time in which a court case can get appealed. #[pallet::constant] type CourtAppealPeriod: Get; - #[pallet::constant] - type CrowdfundMinThreshold: Get>; - - /// The slash percentage if the vote gets revealed during the voting period. + /// The slash percentage if a secret vote gets revealed during the voting period. #[pallet::constant] type DenounceSlashPercentage: Get; + /// The functionality to allow controlling the markets resolution time. type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, @@ -110,15 +115,17 @@ mod pallet { #[pallet::constant] type MaxAppeals: Get; - /// The maximum number of random selected jurors for a dispute. + /// The maximum number of randomly selected jurors for a dispute. #[pallet::constant] type MaxDrawings: Get; + /// The maximum number of jurors that can be registered. #[pallet::constant] type MaxJurors: Get; + /// The minimum stake a user needs to reserve to become a juror. #[pallet::constant] - type MinStake: Get>; + type MinJurorStake: Get>; /// Identifier of this pallet #[pallet::constant] @@ -127,6 +134,7 @@ mod pallet { /// Randomness source type Random: Randomness; + /// The percentage that is slashed if a juror did not vote for the plurality outcome. #[pallet::constant] type RedistributionPercentage: Get; @@ -134,10 +142,6 @@ mod pallet { #[pallet::constant] type SlashPercentage: Get; - /// Weight used to calculate the necessary staking amount to become a juror - #[pallet::constant] - type StakeWeight: Get>; - /// Slashed funds are send to the treasury #[pallet::constant] type TreasuryPalletId: Get; @@ -150,7 +154,7 @@ mod pallet { const INITIAL_JURORS_NUM: usize = 3; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); - // Weight used to increase the number of jurors for subsequent disputes + // Weight used to increase the number of jurors for subsequent appeals // of the same market const SUBSEQUENT_JURORS_FACTOR: usize = 2; @@ -187,10 +191,11 @@ mod pallet { #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); - /// Accounts that stake funds to decide outcomes. + /// The pool of jurors who can get randomly selected according to their stake. #[pallet::storage] pub type JurorPool = StorageValue<_, JurorPoolOf, ValueQuery>; + /// The general information about each juror. #[pallet::storage] pub type Jurors = StorageMap<_, Blake2_128Concat, T::AccountId, JurorInfoOf, OptionQuery>; @@ -199,10 +204,12 @@ mod pallet { #[pallet::storage] pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; + /// The randomly selected jurors with their vote. #[pallet::storage] pub type Drawings = StorageMap<_, Blake2_128Concat, MarketIdOf, DrawingsOf, ValueQuery>; + /// The general information about each court. #[pallet::storage] pub type Courts = StorageMap<_, Blake2_128Concat, MarketIdOf, CourtOf, OptionQuery>; @@ -242,6 +249,8 @@ mod pallet { AppealBackingChecked { market_id: MarketIdOf }, /// A market has been appealed. MarketAppealed { market_id: MarketIdOf, appeal_number: u8 }, + /// The juror stakes have been reassigned. + JurorStakesReassigned { market_id: MarketIdOf }, } #[pallet::error] @@ -272,30 +281,69 @@ mod pallet { NotInAppealPeriod, /// The court is already present for this market. CourtAlreadyExists, + /// For this appeal round the random juror selection extrinsic was already called. JurorsAlreadyDrawn, - AppealAlreadyFunded, + /// For this appeal round the backing check extrinsic was already called. + AppealAlreadyBacked, + /// In order to start an appeal the backing check extrinsic must be called first. CheckBackingFirst, + /// The final appeal extrinsic can only be called after the backing check extrinsic + /// and random selection of jurors for this appeal. AppealNotReady, + /// The caller of this extrinsic must be a randomly selected juror. OnlyDrawnJurorsCanVote, - BelowMinStake, + /// The amount is below the minimum required stake. + BelowMinJurorStake, + /// The maximum number of possible jurors has been reached. MaxJurorsReached, + /// In order to exit the court the juror must not be randomly selected in an active appeal. JurorStillDrawn, + /// In order to exit the court the juror has to exit + /// the pool first with `prepare_exit_court`. JurorNotPreparedToExit, + /// The juror was not found in the pool. This happens after `prepare_exit_court` + /// or if the juror did not vote for plurality decisions. + /// The juror needs to exit the court and then rejoin. JurorNeedsToExit, + /// The juror was not randomly selected for the court. JurorNotDrawn, + /// The juror was drawn but did not manage to secretly vote within the court. JurorNotVoted, + /// The juror was already denounced. This action can only happen once. VoteAlreadyDenounced, + /// A juror tried to denounce herself. SelfDenounceDisallowed, + /// In order to execute the binary search efficiently + /// the join amount must be unqiue for each juror. AmountAlreadyUsed, + /// The court is not in the closed state. + CourtNotClosed, + /// The jurors were already reassigned. + JurorsAlreadyReassigned, + /// The tardy jurors were already punished. + TardyJurorsAlreadyPunished, + /// Punish the tardy jurors first. + PunishTardyJurorsFirst, } #[pallet::call] impl Pallet { + /// Join to become a juror, who is able to get randomly selected + /// for court cases according to the provided stake. + /// The probability to get selected is higher the more funds are staked. + /// + /// # Arguments + /// + /// - `amount`: The amount associated with the joining juror. + /// + /// # Weight + /// + /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. #[pallet::weight(T::WeightInfo::join_court())] #[transactional] pub fn join_court(origin: OriginFor, amount: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(amount >= T::MinStake::get(), Error::::BelowMinStake); + ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); let mut jurors = JurorPool::::get(); @@ -306,7 +354,9 @@ mod pallet { { jurors.remove(i); } else { - // this happens if the juror was slashed by the vote aggregation + // this happens if the juror behaved incorrectly + // (was denounced, did not reveal, did not vote) + // or if `prepare_exit_court` was called return Err(Error::::JurorNeedsToExit.into()); } @@ -333,6 +383,12 @@ mod pallet { Ok(()) } + /// Prepare as a juror to exit the court. + /// For this the juror has to be removed from the stake weighted pool first before the exit. + /// + /// # Weight + /// + /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. #[pallet::weight(T::WeightInfo::exit_court())] #[transactional] pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { @@ -355,6 +411,20 @@ mod pallet { Ok(()) } + /// Remove a juror from all courts. + /// This is only possible if the juror is not part of the pool anymore + /// (with `prepare_exit_court` or was denounced, did not reveal, did not vote) + /// and the juror is not bonded in active courts anymore. + /// + /// # Arguments + /// + /// - `juror`: The juror, who is assumed to be not be part of the pool anymore. + /// + /// # Weight + /// + /// Complexity: `O(n * m)`, where `n` is the number of markets + /// which have active random selections in place, and `m` is the number of jurors + /// randomly selected for each market. #[pallet::weight(T::WeightInfo::exit_court())] #[transactional] pub fn exit_court(origin: OriginFor, juror: AccountIdLookupOf) -> DispatchResult { @@ -384,6 +454,17 @@ mod pallet { Ok(()) } + /// Vote as a randomly selected juror for a specific court case. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// - `secret_vote`: A hash which consists of `juror ++ outcome ++ salt`. + /// + /// # Weight + /// + /// Complexity: `O(n)`, where `n` is the number of jurors + /// in the list of random selections (drawings). #[pallet::weight(T::WeightInfo::vote())] #[transactional] pub fn vote( @@ -393,14 +474,14 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( court.periods.backing_end < now && now <= court.periods.vote_end, Error::::NotInVotingPeriod ); - let mut drawings = >::get(&market_id); + let mut drawings = >::get(market_id); match drawings.iter().position(|(juror, _)| juror == &who) { Some(index) => { let vote = Vote::Secret { secret: secret_vote }; @@ -409,12 +490,29 @@ mod pallet { None => return Err(Error::::OnlyDrawnJurorsCanVote.into()), } - >::insert(&market_id, drawings); + >::insert(market_id, drawings); Self::deposit_event(Event::JurorVoted { juror: who, market_id, secret: secret_vote }); Ok(()) } + /// Denounce a juror during the voting period for which the secret vote is known. + /// This is useful to punish the behaviour that jurors reveal + /// their secrets before the voting period ends. + /// A check of `secret_hash == hash(juror ++ outcome ++ salt)` is performed for validation. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// - `juror`: The juror whose secret vote might be known. + /// - `outcome`: The raw vote outcome which should match with the secret of the juror. + /// - `salt`: The hash which is used to proof that the juror did reveal + /// her vote during the voting period. + /// + /// # Weight + /// + /// Complexity: `O(n)`, where `n` is the number of jurors + /// in the list of random selections (drawings). #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn denounce_vote( @@ -432,7 +530,7 @@ mod pallet { let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; - let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); // ensure in vote period ensure!( @@ -440,7 +538,7 @@ mod pallet { Error::::NotInVotingPeriod ); - let mut drawings = >::get(&market_id); + let mut drawings = >::get(market_id); let (index, vote) = match drawings.iter().position(|(j, _)| j == &juror) { Some(index) => (index, drawings[index].1.clone()), None => return Err(Error::::JurorNotDrawn.into()), @@ -465,7 +563,6 @@ mod pallet { let treasury_account_id = Self::treasury_account_id(); let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), &juror); - // TODO: should the denouncer profit from this? let slash = T::DenounceSlashPercentage::get() * all_reserved; let _ = CurrencyOf::::repatriate_reserved_named( &Self::reserve_id(), @@ -484,7 +581,7 @@ mod pallet { let raw_vote = Vote::Denounced { secret, outcome: outcome.clone(), salt }; drawings[index] = (juror.clone(), raw_vote); - >::insert(&market_id, drawings); + >::insert(market_id, drawings); Self::deposit_event(Event::DenouncedJurorVote { denouncer, @@ -496,6 +593,19 @@ mod pallet { Ok(()) } + /// Reveal the secret vote of the caller juror. + /// A check of `secret_hash == hash(juror ++ outcome ++ salt)` is performed for validation. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// - `outcome`: The raw vote outcome which should match with the secret of the juror. + /// - `salt`: The hash which is used for the validation. + /// + /// # Weight + /// + /// Complexity: `O(n)`, where `n` is the number of jurors + /// in the list of random selections (drawings). #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn reveal_vote( @@ -507,14 +617,14 @@ mod pallet { let who = ensure_signed(origin)?; ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); - let court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( court.periods.vote_end < now && now <= court.periods.aggregation_end, Error::::NotInAggregationPeriod ); - let mut drawings = >::get(&market_id); + let mut drawings = >::get(market_id); let (index, vote) = match drawings.iter().position(|(juror, _)| juror == &who) { Some(index) => (index, drawings[index].1.clone()), None => return Err(Error::::JurorNotDrawn.into()), @@ -544,12 +654,21 @@ mod pallet { let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; drawings[index] = (who.clone(), raw_vote); - >::insert(&market_id, drawings); + >::insert(market_id, drawings); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); Ok(()) } + /// Check if the appeal of a court is allowed to get initiated. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// + /// # Weight + /// + /// Complexity: `O(1)` #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn check_appeal_backing( @@ -558,19 +677,33 @@ mod pallet { ) -> DispatchResult { T::AppealOrigin::ensure_origin(origin)?; - let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; - ensure!(!court.appeal_info.is_backed, Error::::AppealAlreadyFunded); + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + ensure!(!court.appeal_info.is_backed, Error::::AppealAlreadyBacked); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; court.appeal_info.is_backed = true; - >::insert(&market_id, court); + >::insert(market_id, court); Self::deposit_event(Event::AppealBackingChecked { market_id }); Ok(()) } + /// Randomly select jurors from the pool according to their stake + /// for the coming appeal round. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// + /// # Weight + /// + /// Complexity: `O(n)`, where `n` depends on `choose_multiple_weighted` of `select_jurors`. + /// The complexity of this method depends on the feature partition_at_index. + /// If the feature is enabled, then for slices of length n, + /// the complexity is O(n) space and O(n) time. + /// Otherwise, the complexity is O(n) space and O(n * log amount) time. #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn draw_appeal_jurors( @@ -579,36 +712,51 @@ mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; ensure!(!court.appeal_info.is_drawn, Error::::JurorsAlreadyDrawn); ensure!(court.appeal_info.is_backed, Error::::CheckBackingFirst); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; + let last_resolve_at = court.periods.appeal_end; + let appeal_number = court.appeal_info.current as usize; Self::select_jurors(&market_id, appeal_number); + // at the time of flushing the last drawings in `select_jurors` + // we want to avoid the resolution before the full appeal is executed + // otherwise `draw_appeal_jurors` would replace all votes with `Vote::Drawn` + // So, the appeal is inevitable after the call to this extrinsic + // otherwise the market is not going to resolve + let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); court.appeal_info.is_drawn = true; - >::insert(&market_id, court); + >::insert(market_id, court); Self::deposit_event(Event::AppealJurorsDrawn { market_id }); Ok(()) } + /// Trigger an appeal for a court. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// + /// # Weight + /// + /// Complexity: `O(n)`, where `n` is the number of market ids + /// inside the dispute resolution list. #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { ensure_signed(origin)?; - let mut court = >::get(&market_id).ok_or(Error::::CourtNotFound)?; + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; ensure!(court.appeal_info.is_appeal_ready(), Error::::AppealNotReady); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - let last_resolve_at = court.periods.appeal_end; - let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); - let periods = Periods { backing_end: T::CourtBackingPeriod::get(), vote_end: T::CourtVotePeriod::get(), @@ -629,59 +777,220 @@ mod pallet { Ok(()) } + + /// After the court is closed (resolution happened), the tardy jurors can get punished. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// + /// # Weight + /// + /// Complexity: `O(n)`, where `n` is the number of randomly selected jurors for this court. + #[pallet::weight(1_000_000_000_000)] + #[transactional] + pub fn punish_tardy_jurors( + origin: OriginFor, + market_id: MarketIdOf, + ) -> DispatchResult { + ensure_signed(origin)?; + + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let winner = match court.status { + CourtStatus::Closed { winner, punished, reassigned } => { + ensure!(!punished, Error::::TardyJurorsAlreadyPunished); + ensure!(!reassigned, Error::::JurorsAlreadyReassigned); + winner + } + _ => return Err(Error::::CourtNotClosed.into()), + }; + + let treasury_account_id = Self::treasury_account_id(); + + let mut jurors = JurorPool::::get(); + let mut slash_and_remove_juror = |ai: &T::AccountId| { + let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), ai); + let slash = T::SlashPercentage::get() * all_reserved; + let res = CurrencyOf::::repatriate_reserved_named( + &Self::reserve_id(), + ai, + &treasury_account_id, + slash, + BalanceStatus::Free, + ); + if let Err(e) = res { + log::warn!( + "Failed to slash juror {:?} for market {:?}: {:?}", + ai, + market_id, + e + ); + debug_assert!(false); + } + + if let Some(prev_juror_info) = >::get(ai) { + if let Ok(i) = + jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + { + // remove from juror list to prevent being drawn + jurors.remove(i); + } + } else { + log::warn!("Juror {:?} not found in Jurors storage for vote aggregation.", ai); + debug_assert!(false); + } + }; + + for (juror, vote) in Drawings::::get(market_id).iter() { + match vote { + Vote::Drawn => { + slash_and_remove_juror(juror); + } + Vote::Secret { secret: _ } => { + slash_and_remove_juror(juror); + } + // denounce extrinsic already punished the juror + Vote::Denounced { secret: _, outcome: _, salt: _ } => (), + Vote::Revealed { secret: _, outcome: _, salt: _ } => (), + } + } + + court.status = CourtStatus::Closed { winner, reassigned: false, punished: true }; + >::insert(market_id, court); + >::put(jurors); + + Self::deposit_event(Event::JurorStakesReassigned { market_id }); + + Ok(()) + } + + /// After the court is closed (resolution happened) and the tardy jurors have been punished, + /// the juror stakes can get reassigned. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// + /// # Weight + /// + /// Complexity: `O(n)`, where `n` is the number of randomly selected jurors for this court. + #[pallet::weight(1_000_000_000_000)] + #[transactional] + pub fn reassign_juror_stakes( + origin: OriginFor, + market_id: MarketIdOf, + ) -> DispatchResult { + ensure_signed(origin)?; + + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let winner = match court.status { + CourtStatus::Closed { winner, punished, reassigned } => { + ensure!(!reassigned, Error::::JurorsAlreadyReassigned); + ensure!(punished, Error::::PunishTardyJurorsFirst); + winner + } + _ => return Err(Error::::CourtNotClosed.into()), + }; + + let drawings = Drawings::::get(market_id); + + let mut valid_winners_and_losers = Vec::with_capacity(drawings.len()); + + for (juror, vote) in drawings { + if let Vote::Revealed { secret: _, outcome, salt: _ } = vote { + valid_winners_and_losers.push((juror, outcome)); + } + } + + Self::slash_losers_to_award_winners(valid_winners_and_losers.as_slice(), &winner)?; + + court.status = CourtStatus::Closed { winner, punished: true, reassigned: true }; + >::insert(market_id, court); + >::remove(market_id); + + Self::deposit_event(Event::JurorStakesReassigned { market_id }); + + Ok(()) + } } impl Pallet where T: Config, { + pub(crate) fn choose_multiple_weighted( + mut jurors: Vec<(BalanceOf, T::AccountId)>, + number: usize, + rng: &mut R, + ) -> Vec<(T::AccountId, Vote)> { + use core::cmp::Ordering; + use rand::Rng; + + let mut total_weight: u128 = + jurors.iter().map(|(stake, _)| (*stake).saturated_into::()).sum(); + + let mut selected = Vec::with_capacity(number); + + for _ in 0..number { + let mut remaining_weight = total_weight; + let mut index: usize = 0; + let threshold = rng.gen_range(0u128..total_weight); + + let weight_0: u128 = jurors[index].0.saturated_into(); + while remaining_weight > weight_0 { + remaining_weight = remaining_weight.saturating_sub(weight_0); + index = index.saturating_add(1); + } + + if threshold < remaining_weight { + selected.push((jurors[index].1.clone(), Vote::Drawn)); + total_weight = total_weight.saturating_sub(weight_0); + jurors.swap_remove(index); + } else { + for i in 0..jurors.len() { + let weight_1: u128 = jurors[i].0.saturated_into(); + match threshold.cmp(&remaining_weight) { + Ordering::Less => { + remaining_weight = remaining_weight.saturating_sub(weight_1); + } + Ordering::Equal => { + selected.push((jurors[i].1.clone(), Vote::Drawn)); + total_weight = total_weight.saturating_sub(weight_1); + jurors.swap_remove(i); + break; + } + Ordering::Greater => { + selected.push((jurors[i].1.clone(), Vote::Drawn)); + total_weight = total_weight.saturating_sub(weight_1); + jurors.swap_remove(i); + break; + } + } + } + } + + if jurors.is_empty() { + break; + } + } + + selected + } + pub(crate) fn select_jurors(market_id: &MarketIdOf, appeal_number: usize) { let jurors: JurorPoolOf = JurorPool::::get(); let necessary_jurors_num = Self::necessary_jurors_num(appeal_number); let mut rng = Self::rng(); - let actual_len = - jurors.len().min(necessary_jurors_num).min(T::MaxDrawings::get() as usize); - - let random_jurors = jurors - .choose_multiple_weighted(&mut rng, actual_len, |item| { - let stake = item.0.saturated_into::(); - // split the u128 bits into two u64 bits and convert them to f64 - // f64 representation is only used to get weighted random selection - let lo = (stake & 0xFFFFFFFFFFFFFFFF) as u64; - let hi = (stake >> 64) as u64; - - let lo_f64 = f64::from_bits(lo); - let hi_f64 = f64::from_bits(hi); - - hi_f64 * (2.0f64).powi(64) + lo_f64 - }) - .unwrap_or_else(|err| { - log::warn!( - "Court: weighted selection failed, falling back to random selection for \ - market {:?} with error: {:?}.", - market_id, - err - ); - debug_assert!(false); - // fallback to random selection if weighted selection fails - jurors.choose_multiple(&mut rng, actual_len) - }) - .collect::>(); + let actual_len = jurors.len().min(necessary_jurors_num); - let mut drawings = >::get(market_id); - for (_, ai) in random_jurors { - // below or equal MaxDrawings is ensured above - let res = drawings.try_push((ai.clone(), Vote::Drawn)); - if let Err(err) = res { - log::warn!( - "Court: failed to add random juror {:?} to market {:?} with error: {:?}.", - ai, - market_id, - err - ); - debug_assert!(false); - } - } + let random_jurors = + Self::choose_multiple_weighted(jurors.into_inner(), actual_len, &mut rng); + + // we allow at most MaxDrawings jurors + // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 + // MaxDrawings should be 127 in this case + let drawings = >::truncate_from(random_jurors); + // new appeal round should have a fresh set of drawings >::insert(market_id, drawings); } @@ -690,7 +999,7 @@ mod pallet { court: &CourtOf, now: T::BlockNumber, ) -> Result<(), DispatchError> { - let market = T::MarketCommons::market(&market_id)?; + let market = T::MarketCommons::market(market_id)?; ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, @@ -702,8 +1011,10 @@ mod pallet { Error::::NotInAppealPeriod ); - // TODO for now allow as many appeals as needed, later check if max appeals is reached - // TODO return error when max appeals is reached here and only allow global disputes to take over + ensure!( + court.appeal_info.current < court.appeal_info.max, + Error::::MaxAppealsReached + ); Ok(()) } @@ -735,10 +1046,7 @@ mod pallet { T::TreasuryPalletId::get().into_account_truncating() } - // Calculates the necessary number of jurors depending on the number of market disputes. - // - // Result is capped to `usize::MAX` or in other words, capped to a very, very, very - // high number of jurors. + // Calculates the necessary number of jurors depending on the number of market appeals. fn necessary_jurors_num(appeals_len: usize) -> usize { // 2^(appeals_len) * 3 + 2^(appeals_len) - 1 // MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 @@ -751,95 +1059,53 @@ mod pallet { } fn slash_losers_to_award_winners( - valid_winners_and_losers: &[(&T::AccountId, &OutcomeReport)], + valid_winners_and_losers: &[(T::AccountId, OutcomeReport)], winner_outcome: &OutcomeReport, ) -> DispatchResult { - let mut total_incentives = BalanceOf::::from(0u8); - let mut total_winners = BalanceOf::::from(0u8); + let mut total_incentives = >::zero(); let mut winners = Vec::with_capacity(valid_winners_and_losers.len()); for (juror, outcome) in valid_winners_and_losers { - if outcome == &winner_outcome { + if outcome == winner_outcome { winners.push(juror); - total_winners = total_winners.saturating_add(BalanceOf::::from(1u8)); } else { let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), juror); let slash = T::RedistributionPercentage::get() * all_reserved; - CurrencyOf::::slash_reserved_named(&Self::reserve_id(), juror, slash); - total_incentives = total_incentives.saturating_add(slash); + let (imb, _excess) = + CurrencyOf::::slash_reserved_named(&Self::reserve_id(), juror, slash); + total_incentives.subsume(imb); } } - let individual_winner_incentive = - if let Some(e) = total_incentives.checked_div(&total_winners) { - e - } else { - // No winners - return Ok(()); - }; - - for juror in winners { - CurrencyOf::::deposit_into_existing(juror, individual_winner_incentive)?; + if let Some(reward_per_each) = + total_incentives.peek().checked_div(&winners.len().saturated_into()) + { + for juror in winners { + let (actual_reward, leftover) = total_incentives.split(reward_per_each); + total_incentives = leftover; + CurrencyOf::::resolve_creating(juror, actual_reward); + } + } else { + // if there are no winners reward the treasury + let treasury_acc = Self::treasury_account_id(); + CurrencyOf::::resolve_creating(&treasury_acc, total_incentives); } Ok(()) } - fn aggregate( + fn get_winner( votes: &[(T::AccountId, Vote)], - ) -> Result<(OutcomeReport, Vec<(&T::AccountId, &OutcomeReport)>), DispatchError> { + ) -> Result { let mut scores = BTreeMap::::new(); - let treasury_account_id = Self::treasury_account_id(); - - let slash_and_remove_juror = |ai: &T::AccountId| { - let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), ai); - let slash = T::SlashPercentage::get() * all_reserved; - let _ = CurrencyOf::::repatriate_reserved_named( - &Self::reserve_id(), - ai, - &treasury_account_id, - slash, - BalanceStatus::Free, - )?; - - if let Some(prev_juror_info) = >::get(ai) { - let mut jurors = JurorPool::::get(); - if let Ok(i) = - jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) - { - // remove from juror list to prevent being drawn - jurors.remove(i); - >::put(jurors); - } - } else { - log::warn!("Juror {:?} not found in Jurors storage for vote aggregation.", ai); - debug_assert!(false); - } - - Ok::<_, DispatchError>(()) - }; - - let mut valid_winners_and_losers = Vec::with_capacity(votes.len()); - - for (juror, vote) in votes { - match vote { - Vote::Drawn => { - slash_and_remove_juror(juror)?; - } - Vote::Secret { secret: _ } => { - slash_and_remove_juror(juror)?; - } - // denounce extrinsic already slashed the juror - Vote::Denounced { secret: _, outcome: _, salt: _ } => (), - Vote::Revealed { secret: _, outcome, salt: _ } => { - if let Some(el) = scores.get_mut(&outcome) { - *el = el.saturating_add(1); - } else { - scores.insert(outcome.clone(), 1); - } - valid_winners_and_losers.push((juror, outcome)); + for (_juror, vote) in votes { + if let Vote::Revealed { secret: _, outcome, salt: _ } = vote { + if let Some(el) = scores.get_mut(outcome) { + *el = el.saturating_add(1); + } else { + scores.insert(outcome.clone(), 1); } } } @@ -849,6 +1115,7 @@ mod pallet { let mut best_score = if let Some(first) = iter.next() { first } else { + // TODO(#980): Create another voting round return Err(Error::::NoVotes.into()); }; @@ -858,7 +1125,7 @@ mod pallet { } } - Ok((best_score.0.clone(), valid_winners_and_losers)) + Ok(best_score.0.clone()) } } @@ -897,7 +1164,7 @@ mod pallet { Self::select_jurors(market_id, 0usize); let _ids_len = - T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; + T::DisputeResolution::add_auto_resolve(market_id, court.periods.appeal_end)?; >::insert(market_id, court); @@ -912,11 +1179,18 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - let drawings: Vec<_> = Drawings::::get(market_id).into_inner(); - let (winner_outcome, valid_winners_and_losers) = Self::aggregate(drawings.as_slice())?; - Self::slash_losers_to_award_winners(&valid_winners_and_losers, &winner_outcome)?; - >::remove(market_id); + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + + let drawings = Drawings::::get(market_id); + let winner_outcome = Self::get_winner(drawings.as_slice())?; + + court.status = CourtStatus::Closed { + winner: winner_outcome.clone(), + punished: false, + reassigned: false, + }; + >::insert(market_id, court); Ok(Some(winner_outcome)) } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 7048587ac..1b38c134d 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -31,8 +31,10 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, CourtCaseDuration, CourtPalletId, MaxReserves, MinimumPeriod, PmPalletId, - StakeWeight, BASE, + BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, CourtBackingPeriod, + CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDrawings, + MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, + SlashPercentage, BASE, }, traits::DisputeResolutionApi, types::{ @@ -44,6 +46,12 @@ use zeitgeist_primitives::{ pub const ALICE: AccountIdTest = 0; pub const BOB: AccountIdTest = 1; pub const CHARLIE: AccountIdTest = 2; +pub const DAVE: AccountIdTest = 3; +pub const EVE: AccountIdTest = 4; +pub const FERDIE: AccountIdTest = 5; +pub const GINA: AccountIdTest = 6; +pub const HARRY: AccountIdTest = 7; +pub const IAN: AccountIdTest = 8; pub const INITIAL_BALANCE: u128 = 1000 * BASE; parameter_types! { @@ -106,12 +114,23 @@ impl DisputeResolutionApi for NoopResolution { } impl crate::Config for Runtime { + type AppealOrigin = frame_system::EnsureSigned; + type CourtBackingPeriod = CourtBackingPeriod; + type CourtVotePeriod = CourtVotePeriod; + type CourtAggregationPeriod = CourtAggregationPeriod; + type CourtAppealPeriod = CourtAppealPeriod; + type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = NoopResolution; type Event = (); type MarketCommons = MarketCommons; + type MaxAppeals = MaxAppeals; + type MaxDrawings = MaxDrawings; + type MaxJurors = MaxJurors; + type MinJurorStake = MinJurorStake; type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; - type StakeWeight = StakeWeight; + type RedistributionPercentage = RedistributionPercentage; + type SlashPercentage = SlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 72aeb6b30..b2cc624fe 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -20,9 +20,9 @@ use crate::{ mock::{ Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, - System, ALICE, BOB, CHARLIE, INITIAL_BALANCE, + System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, }, - Error, Juror, JurorStatus, Jurors, MarketOf, RequestedJurors, Votes, + Error, JurorInfo, Jurors, MarketOf, }; use frame_support::{ assert_noop, assert_ok, @@ -32,8 +32,8 @@ use zeitgeist_primitives::{ constants::BASE, traits::DisputeApi, types::{ - Asset, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, - MarketPeriod, MarketStatus, MarketType, OutcomeReport, ScoringRule, + AccountIdTest, Asset, Deadlines, Market, MarketBonds, MarketCreation, + MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, ScoringRule, }, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -55,24 +55,29 @@ const DEFAULT_MARKET: MarketOf = Market { scoring_rule: ScoringRule::CPMM, bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None }, }; -const DEFAULT_SET_OF_JURORS: &[(u128, Juror)] = &[ - (7, Juror { status: JurorStatus::Ok }), - (6, Juror { status: JurorStatus::Tardy }), - (5, Juror { status: JurorStatus::Ok }), - (4, Juror { status: JurorStatus::Tardy }), - (3, Juror { status: JurorStatus::Ok }), - (2, Juror { status: JurorStatus::Ok }), - (1, Juror { status: JurorStatus::Ok }), + +const DEFAULT_SET_OF_JURORS: &[(u128, AccountIdTest)] = &[ + (9, HARRY), + (8, IAN), + (7, ALICE), + (6, BOB), + (5, CHARLIE), + (4, DAVE), + (3, EVE), + (2, FERDIE), + (1, GINA), ]; #[test] fn exit_court_successfully_removes_a_juror_and_frees_balances() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(Court::join_court(Origin::signed(ALICE))); + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!(Jurors::::iter().count(), 1); assert_eq!(Balances::free_balance(ALICE), 998 * BASE); - assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), 2 * BASE); - assert_ok!(Court::exit_court(Origin::signed(ALICE))); + assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), amount); + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); assert_eq!(Jurors::::iter().count(), 0); assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE); assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), 0); @@ -80,49 +85,21 @@ fn exit_court_successfully_removes_a_juror_and_frees_balances() { } #[test] -fn exit_court_will_not_remove_an_unknown_juror() { +fn prepare_exit_court_will_not_remove_an_unknown_juror() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Court::exit_court(Origin::signed(ALICE)), + Court::prepare_exit_court(Origin::signed(ALICE)), Error::::JurorDoesNotExists ); }); } -#[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_named(&Court::reserve_id(), &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_named(&Court::reserve_id(), &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 { status: JurorStatus::Ok }) - ); - }); -} - -#[test] -fn join_court_will_not_insert_an_already_stored_juror() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(Court::join_court(Origin::signed(ALICE))); - assert_noop!( - Court::join_court(Origin::signed(ALICE)), - Error::::JurorAlreadyExists - ); + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_eq!(Jurors::::iter().next().unwrap(), (ALICE, JurorInfo { stake: amount })); }); } @@ -154,15 +131,6 @@ fn get_resolution_outcome_denies_non_court_markets() { fn appeal_stores_jurors_that_should_vote() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); - let _ = Court::join_court(Origin::signed(ALICE)); - let _ = Court::join_court(Origin::signed(BOB)); - MarketCommons::push_market(DEFAULT_MARKET).unwrap(); - Court::appeal(Origin::signed(ALICE), 0).unwrap(); - assert_noop!( - Court::join_court(Origin::signed(ALICE)), - Error::::JurorAlreadyExists - ); - assert_eq!(RequestedJurors::::iter().count(), 2); }); } @@ -171,107 +139,27 @@ fn appeal_stores_jurors_that_should_vote() { fn get_resolution_outcome_awards_winners_and_slashes_losers() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); - Court::join_court(Origin::signed(ALICE)).unwrap(); - Court::join_court(Origin::signed(BOB)).unwrap(); - Court::join_court(Origin::signed(CHARLIE)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET).unwrap(); - Court::appeal(Origin::signed(ALICE), 0).unwrap(); - Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(2)).unwrap(); - Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(3)).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); - assert_eq!(Balances::free_balance(ALICE), 998 * BASE + 3 * BASE); - assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), 2 * BASE); - assert_eq!(Balances::free_balance(BOB), 996 * BASE); - assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &BOB), 4 * BASE); - assert_eq!(Balances::free_balance(CHARLIE), 994 * BASE); - assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &CHARLIE), 3 * BASE); - }); -} - -#[test] -fn get_resolution_outcome_decides_market_outcome_based_on_the_majority() { - ExtBuilder::default().build().execute_with(|| { - setup_blocks(2); - Court::join_court(Origin::signed(ALICE)).unwrap(); - Court::join_court(Origin::signed(BOB)).unwrap(); - Court::join_court(Origin::signed(CHARLIE)).unwrap(); + let amount_alice = 2 * BASE; + let amount_bob = 3 * BASE; + let amount_charlie = 4 * BASE; + Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); + Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); + Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); MarketCommons::push_market(DEFAULT_MARKET).unwrap(); - Court::appeal(Origin::signed(ALICE), 0).unwrap(); - Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let outcome = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); - assert_eq!(outcome, Some(OutcomeReport::Scalar(1))); }); } #[test] -fn get_resolution_outcome_sets_late_jurors_as_tardy() { +fn get_resolution_outcome_decides_market_outcome_based_on_the_plurality() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); - Court::join_court(Origin::signed(ALICE)).unwrap(); - Court::join_court(Origin::signed(BOB)).unwrap(); - Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); + let amount_alice = 2 * BASE; + let amount_bob = 3 * BASE; + let amount_charlie = 4 * BASE; + Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); + Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); + Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); MarketCommons::push_market(DEFAULT_MARKET).unwrap(); - Court::appeal(Origin::signed(ALICE), 0).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); - assert_eq!(Jurors::::get(ALICE).unwrap().status, JurorStatus::Ok); - assert_eq!(Jurors::::get(BOB).unwrap().status, JurorStatus::Tardy); - }); -} - -#[test] -fn get_resolution_outcome_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tardy() { - ExtBuilder::default().build().execute_with(|| { - setup_blocks(2); - Court::join_court(Origin::signed(ALICE)).unwrap(); - Court::join_court(Origin::signed(BOB)).unwrap(); - Court::join_court(Origin::signed(CHARLIE)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET).unwrap(); - Court::appeal(Origin::signed(ALICE), 0).unwrap(); - Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); - assert_eq!(Jurors::::get(CHARLIE).unwrap().status, JurorStatus::Tardy); - }); -} - -#[test] -fn get_resolution_outcome_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { - ExtBuilder::default().build().execute_with(|| { - setup_blocks(2); - Court::join_court(Origin::signed(ALICE)).unwrap(); - Court::join_court(Origin::signed(BOB)).unwrap(); - Court::set_stored_juror_as_tardy(&BOB).unwrap(); - Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET).unwrap(); - Court::appeal(Origin::signed(ALICE), 0).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); - let join_court_stake = 40000000000; - let slash = join_court_stake / 5; - assert_eq!(Balances::free_balance(Court::treasury_account_id()), INITIAL_BALANCE + slash); - assert_eq!(Balances::free_balance(BOB), INITIAL_BALANCE - slash); - assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &BOB), 0); - }); -} - -#[test] -fn get_resolution_outcome_removes_requested_jurors_and_votes() { - ExtBuilder::default().build().execute_with(|| { - setup_blocks(2); - Court::join_court(Origin::signed(ALICE)).unwrap(); - Court::join_court(Origin::signed(BOB)).unwrap(); - Court::join_court(Origin::signed(CHARLIE)).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET).unwrap(); - Court::appeal(Origin::signed(ALICE), 0).unwrap(); - Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); - Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); - assert_eq!(RequestedJurors::::iter().count(), 0); - assert_eq!(Votes::::iter().count(), 0); }); } @@ -281,14 +169,15 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = Court::random_jurors(DEFAULT_SET_OF_JURORS, 2, &mut rng); + let random_jurors = + Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS.to_vec(), 2, &mut rng); let mut at_least_one_set_is_different = false; for _ in 0..100 { setup_blocks(1); let another_set_of_random_jurors = - Court::random_jurors(DEFAULT_SET_OF_JURORS, 2, &mut rng); + Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS.to_vec(), 2, &mut rng); let mut iter = another_set_of_random_jurors.iter(); if let Some(juror) = iter.next() { @@ -314,9 +203,10 @@ fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = Court::random_jurors(DEFAULT_SET_OF_JURORS, 2, &mut rng); - for (_, juror) in random_jurors { - assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| &el.1 == juror)); + let random_jurors = + Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS.to_vec(), 2, &mut rng); + for (juror, _) in random_jurors { + assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.1 == juror)); } }); } @@ -324,19 +214,8 @@ fn random_jurors_returns_a_subset_of_jurors() { #[test] fn vote_will_not_accept_unknown_accounts() { ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(0)), - Error::::OnlyJurorsCanVote - ); - }); -} - -#[test] -fn vote_will_stored_outcome_from_a_juror() { - ExtBuilder::default().build().execute_with(|| { - let _ = Court::join_court(Origin::signed(ALICE)); - let _ = Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(0)); - assert_eq!(Votes::::get(ALICE, 0).unwrap(), (0, OutcomeReport::Scalar(0))); + setup_blocks(123); + Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); }); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 4870ab4ef..80764e234 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -88,6 +88,21 @@ impl AppealInfo { } } +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub enum CourtStatus { + Open, + Closed { winner: OutcomeReport, punished: bool, reassigned: bool }, +} + #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -99,8 +114,8 @@ impl AppealInfo { Eq, )] pub struct CourtInfo { + pub(crate) status: CourtStatus, pub(crate) appeal_info: AppealInfo, - pub(crate) winner: Option, pub(crate) periods: Periods, } @@ -111,9 +126,11 @@ impl CourtInfo let aggregation_end = vote_end.saturating_add(periods.aggregation_end); let appeal_end = aggregation_end.saturating_add(periods.appeal_end); let periods = Periods { backing_end, vote_end, aggregation_end, appeal_end }; + // 2^1 * 3 + 2^1 - 1 = 7 jurors in the first appeal round (`necessary_jurors_num`) let appeal_info = AppealInfo { current: 1, max: max_appeals, is_drawn: false, is_backed: false }; - Self { appeal_info, winner: None, periods } + let status = CourtStatus::Open; + Self { status, appeal_info, periods } } pub fn update_periods(&mut self, periods: Periods, now: BlockNumber) { diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index f8453aca6..36d4e8a55 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -38,15 +38,16 @@ use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; use zeitgeist_primitives::{ constants::mock::{ AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, CorrectionPeriod, - CourtCaseDuration, CourtPalletId, ExistentialDeposit, ExistentialDeposits, ExitFee, - GetNativeCurrencyId, LiquidityMiningPalletId, MaxApprovals, MaxAssets, MaxCategories, - MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, - MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, - MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, - MinDisputeDuration, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, - MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, - SimpleDisputesPalletId, StakeWeight, SwapsPalletId, TreasuryPalletId, BASE, CENT, - MILLISECS_PER_BLOCK, + CourtAggregationPeriod, CourtAppealPeriod, CourtBackingPeriod, CourtPalletId, + CourtVotePeriod, DenounceSlashPercentage, ExistentialDeposit, ExistentialDeposits, ExitFee, + GetNativeCurrencyId, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, + MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDrawings, MaxEditReasonLen, + MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, + MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, + MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, + MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, + OutcomeFactor, OutsiderBond, PmPalletId, RedistributionPercentage, SimpleDisputesPalletId, + SlashPercentage, SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -277,11 +278,22 @@ impl zrml_authorized::Config for Runtime { impl zrml_court::Config for Runtime { type DisputeResolution = prediction_markets::Pallet; + type AppealOrigin = frame_system::EnsureSigned; + type CourtBackingPeriod = CourtBackingPeriod; + type CourtVotePeriod = CourtVotePeriod; + type CourtAggregationPeriod = CourtAggregationPeriod; + type CourtAppealPeriod = CourtAppealPeriod; + type DenounceSlashPercentage = DenounceSlashPercentage; type Event = Event; type MarketCommons = MarketCommons; + type MaxAppeals = MaxAppeals; + type MaxDrawings = MaxDrawings; + type MaxJurors = MaxJurors; + type MinJurorStake = MinJurorStake; type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; - type StakeWeight = StakeWeight; + type RedistributionPercentage = RedistributionPercentage; + type SlashPercentage = SlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } From b2d0c9181019d5781d4ec66fe7240d04d0e39881 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 23 Feb 2023 08:21:57 +0100 Subject: [PATCH 096/368] correct random juror selection --- primitives/src/constants/mock.rs | 11 ++-- runtime/battery-station/src/parameters.rs | 27 ++++++++- runtime/common/src/lib.rs | 16 +++++- runtime/zeitgeist/src/parameters.rs | 27 ++++++++- zrml/court/src/lib.rs | 68 ++++++++--------------- zrml/court/src/tests.rs | 8 +-- 6 files changed, 95 insertions(+), 62 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 5a2250e78..8c024873c 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -19,15 +19,14 @@ parameter_types! { parameter_types! { pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); - pub const StakeWeight: u128 = 2 * BASE; - pub const CourtBackingPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; - pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; - pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; - pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtBackingPeriod: BlockNumber = 34; + pub const CourtVotePeriod: BlockNumber = 34; + pub const CourtAggregationPeriod: BlockNumber = 34; + pub const CourtAppealPeriod: BlockNumber = 34; pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); pub const MaxAppeals: u32 = 5; pub const MaxDrawings: u32 = 127; - pub const MaxJurors: u32 = 10_000; + pub const MaxJurors: u32 = 1000; pub const MinJurorStake: Balance = 2 * BASE; pub const RedistributionPercentage: Percent = Percent::from_percent(50); pub const SlashPercentage: Percent = Percent::from_percent(10); diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 280aa7e12..34e7d179a 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -81,9 +81,30 @@ parameter_types! { pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. pub const CourtPalletId: PalletId = COURT_PALLET_ID; - /// This value is multiplied by the current number of jurors to determine the stake - /// the juror has to pay. - pub const StakeWeight: u128 = 2 * BASE; + /// The time to wait before jurors can start voting. + /// The intention is to use this period as preparation time + /// (for example vote outcome addition through crowdfunding) + pub const CourtBackingPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The time in which the jurors can cast their secret vote. + pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The time in which the jurors should reveal their secret vote. + pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The time in which a court case can get appealed. + pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The slash percentage if a secret vote gets revealed during the voting period. + pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); + /// The maximum number of appeals until the court fails. + pub const MaxAppeals: u32 = 5; + /// The maximum number of randomly selected jurors for a dispute. + pub const MaxDrawings: u32 = 127; + /// The maximum number of jurors that can be registered. + pub const MaxJurors: u32 = 10_000; + /// The minimum stake a user needs to reserve to become a juror. + pub const MinJurorStake: Balance = 2 * BASE; + /// The percentage that is slashed if a juror did not vote for the plurality outcome. + pub const RedistributionPercentage: Percent = Percent::from_percent(50); + /// The percentage that is being slashed from the juror's stake. + pub const SlashPercentage: Percent = Percent::from_percent(10); // Democracy /// How often (in blocks) new public referenda are launched. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index f91764419..db8fc9c88 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -935,12 +935,26 @@ macro_rules! impl_config_traits { } impl zrml_court::Config for Runtime { + type AppealOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureMember + >; + type CourtBackingPeriod = CourtBackingPeriod; + type CourtVotePeriod = CourtVotePeriod; + type CourtAggregationPeriod = CourtAggregationPeriod; + type CourtAppealPeriod = CourtAppealPeriod; + type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type MarketCommons = MarketCommons; + type MaxAppeals = MaxAppeals; + type MaxDrawings = MaxDrawings; + type MaxJurors = MaxJurors; + type MinJurorStake = MinJurorStake; type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; - type StakeWeight = StakeWeight; + type RedistributionPercentage = RedistributionPercentage; + type SlashPercentage = SlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index f4173f0d0..46594abc0 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -81,9 +81,30 @@ parameter_types! { pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. pub const CourtPalletId: PalletId = COURT_PALLET_ID; - /// This value is multiplied by the current number of jurors to determine the stake - /// the juror has to pay. - pub const StakeWeight: u128 = 2 * BASE; + /// The time to wait before jurors can start voting. + /// The intention is to use this period as preparation time + /// (for example vote outcome addition through crowdfunding) + pub const CourtBackingPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The time in which the jurors can cast their secret vote. + pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The time in which the jurors should reveal their secret vote. + pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The time in which a court case can get appealed. + pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The slash percentage if a secret vote gets revealed during the voting period. + pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); + /// The maximum number of appeals until the court fails. + pub const MaxAppeals: u32 = 5; + /// The maximum number of randomly selected jurors for a dispute. + pub const MaxDrawings: u32 = 127; + /// The maximum number of jurors that can be registered. + pub const MaxJurors: u32 = 10_000; + /// The minimum stake a user needs to reserve to become a juror. + pub const MinJurorStake: Balance = 2 * BASE; + /// The percentage that is slashed if a juror did not vote for the plurality outcome. + pub const RedistributionPercentage: Percent = Percent::from_percent(50); + /// The percentage that is being slashed from the juror's stake. + pub const SlashPercentage: Percent = Percent::from_percent(10); // Democracy /// How often (in blocks) new public referenda are launched. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9867de7d1..e1cdce357 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -919,58 +919,38 @@ mod pallet { T: Config, { pub(crate) fn choose_multiple_weighted( - mut jurors: Vec<(BalanceOf, T::AccountId)>, + market_id: &MarketIdOf, + jurors: &[(BalanceOf, T::AccountId)], number: usize, rng: &mut R, ) -> Vec<(T::AccountId, Vote)> { - use core::cmp::Ordering; - use rand::Rng; - - let mut total_weight: u128 = - jurors.iter().map(|(stake, _)| (*stake).saturated_into::()).sum(); + use rand::{ + distributions::{Distribution, WeightedIndex}, + seq::SliceRandom, + }; let mut selected = Vec::with_capacity(number); - for _ in 0..number { - let mut remaining_weight = total_weight; - let mut index: usize = 0; - let threshold = rng.gen_range(0u128..total_weight); - - let weight_0: u128 = jurors[index].0.saturated_into(); - while remaining_weight > weight_0 { - remaining_weight = remaining_weight.saturating_sub(weight_0); - index = index.saturating_add(1); - } + let res = WeightedIndex::new(jurors.iter().map(|item| item.0.saturated_into::())); - if threshold < remaining_weight { - selected.push((jurors[index].1.clone(), Vote::Drawn)); - total_weight = total_weight.saturating_sub(weight_0); - jurors.swap_remove(index); - } else { - for i in 0..jurors.len() { - let weight_1: u128 = jurors[i].0.saturated_into(); - match threshold.cmp(&remaining_weight) { - Ordering::Less => { - remaining_weight = remaining_weight.saturating_sub(weight_1); - } - Ordering::Equal => { - selected.push((jurors[i].1.clone(), Vote::Drawn)); - total_weight = total_weight.saturating_sub(weight_1); - jurors.swap_remove(i); - break; - } - Ordering::Greater => { - selected.push((jurors[i].1.clone(), Vote::Drawn)); - total_weight = total_weight.saturating_sub(weight_1); - jurors.swap_remove(i); - break; - } - } + match res { + Ok(distribution) => { + for _ in 0..number { + selected.push((jurors[distribution.sample(rng)].1.clone(), Vote::Drawn)); } } - - if jurors.is_empty() { - break; + Err(err) => { + // this can also happen when there are no jurors + log::warn!( + "Court: weighted selection failed, falling back to random selection for \ + market {:?} with error: {:?}.", + market_id, + err + ); + // fallback to random selection if weighted selection fails + jurors.choose_multiple(rng, number).for_each(|item| { + selected.push((item.1.clone(), Vote::Drawn)); + }); } } @@ -984,7 +964,7 @@ mod pallet { let actual_len = jurors.len().min(necessary_jurors_num); let random_jurors = - Self::choose_multiple_weighted(jurors.into_inner(), actual_len, &mut rng); + Self::choose_multiple_weighted(market_id, jurors.as_slice(), actual_len, &mut rng); // we allow at most MaxDrawings jurors // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index b2cc624fe..b723fa73f 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -169,15 +169,14 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = - Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS.to_vec(), 2, &mut rng); + let random_jurors = Court::choose_multiple_weighted(&0, DEFAULT_SET_OF_JURORS, 2, &mut rng); let mut at_least_one_set_is_different = false; for _ in 0..100 { setup_blocks(1); let another_set_of_random_jurors = - Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS.to_vec(), 2, &mut rng); + Court::choose_multiple_weighted(&0, DEFAULT_SET_OF_JURORS, 2, &mut rng); let mut iter = another_set_of_random_jurors.iter(); if let Some(juror) = iter.next() { @@ -203,8 +202,7 @@ fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = - Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS.to_vec(), 2, &mut rng); + let random_jurors = Court::choose_multiple_weighted(&0, DEFAULT_SET_OF_JURORS, 2, &mut rng); for (juror, _) in random_jurors { assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.1 == juror)); } From 2d1f28cf0109c7101c8d18ab8a84754ba798e550 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 23 Feb 2023 10:30:31 +0100 Subject: [PATCH 097/368] make small improvements and clippy --- primitives/src/constants/mock.rs | 2 +- runtime/battery-station/src/parameters.rs | 2 +- zrml/court/src/lib.rs | 45 +++++++++++++---------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 8c024873c..a08072176 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -27,7 +27,7 @@ parameter_types! { pub const MaxAppeals: u32 = 5; pub const MaxDrawings: u32 = 127; pub const MaxJurors: u32 = 1000; - pub const MinJurorStake: Balance = 2 * BASE; + pub const MinJurorStake: Balance = 5 * BASE; pub const RedistributionPercentage: Percent = Percent::from_percent(50); pub const SlashPercentage: Percent = Percent::from_percent(10); } diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 34e7d179a..3c20d5f8b 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -100,7 +100,7 @@ parameter_types! { /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. - pub const MinJurorStake: Balance = 2 * BASE; + pub const MinJurorStake: Balance = 5 * BASE; /// The percentage that is slashed if a juror did not vote for the plurality outcome. pub const RedistributionPercentage: Percent = Percent::from_percent(50); /// The percentage that is being slashed from the juror's stake. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index e1cdce357..bd70b9f2e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -251,6 +251,8 @@ mod pallet { MarketAppealed { market_id: MarketIdOf, appeal_number: u8 }, /// The juror stakes have been reassigned. JurorStakesReassigned { market_id: MarketIdOf }, + /// The tardy jurors have been punished. + TardyJurorsPunished { market_id: MarketIdOf }, } #[pallet::error] @@ -324,6 +326,8 @@ mod pallet { TardyJurorsAlreadyPunished, /// Punish the tardy jurors first. PunishTardyJurorsFirst, + /// There are not enough jurors in the pool. + NotEnoughJurors, } #[pallet::call] @@ -339,7 +343,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(T::WeightInfo::join_court())] + #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn join_court(origin: OriginFor, amount: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; @@ -360,7 +364,7 @@ mod pallet { return Err(Error::::JurorNeedsToExit.into()); } - juror_info.stake = prev_juror_info.stake.saturating_add(amount); + juror_info.stake = juror_info.stake.saturating_add(prev_juror_info.stake); } match jurors.binary_search_by_key(&juror_info.stake, |tuple| tuple.0) { @@ -369,10 +373,11 @@ mod pallet { // if there are multiple jurors with the same stake Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), Err(i) => jurors - .try_insert(i, (amount, who.clone())) + .try_insert(i, (juror_info.stake, who.clone())) .map_err(|_| Error::::MaxJurorsReached)?, }; + // full reserve = prev_juror_info.stake (already reserved) + amount CurrencyOf::::reserve_named(&Self::reserve_id(), &who, amount)?; JurorPool::::put(jurors); @@ -389,7 +394,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(T::WeightInfo::exit_court())] + #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -425,7 +430,7 @@ mod pallet { /// Complexity: `O(n * m)`, where `n` is the number of markets /// which have active random selections in place, and `m` is the number of jurors /// randomly selected for each market. - #[pallet::weight(T::WeightInfo::exit_court())] + #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn exit_court(origin: OriginFor, juror: AccountIdLookupOf) -> DispatchResult { ensure_signed(origin)?; @@ -465,7 +470,7 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors /// in the list of random selections (drawings). - #[pallet::weight(T::WeightInfo::vote())] + #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn vote( origin: OriginFor, @@ -700,10 +705,6 @@ mod pallet { /// # Weight /// /// Complexity: `O(n)`, where `n` depends on `choose_multiple_weighted` of `select_jurors`. - /// The complexity of this method depends on the feature partition_at_index. - /// If the feature is enabled, then for slices of length n, - /// the complexity is O(n) space and O(n) time. - /// Otherwise, the complexity is O(n) space and O(n * log amount) time. #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn draw_appeal_jurors( @@ -721,7 +722,9 @@ mod pallet { let last_resolve_at = court.periods.appeal_end; let appeal_number = court.appeal_info.current as usize; - Self::select_jurors(&market_id, appeal_number); + let jurors: JurorPoolOf = JurorPool::::get(); + ensure!(jurors.len() >= INITIAL_JURORS_NUM, Error::::NotEnoughJurors); + Self::select_jurors(&market_id, jurors.as_slice(), appeal_number); // at the time of flushing the last drawings in `select_jurors` // we want to avoid the resolution before the full appeal is executed // otherwise `draw_appeal_jurors` would replace all votes with `Vote::Drawn` @@ -797,9 +800,8 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { - CourtStatus::Closed { winner, punished, reassigned } => { + CourtStatus::Closed { winner, punished, reassigned: _ } => { ensure!(!punished, Error::::TardyJurorsAlreadyPunished); - ensure!(!reassigned, Error::::JurorsAlreadyReassigned); winner } _ => return Err(Error::::CourtNotClosed.into()), @@ -859,7 +861,7 @@ mod pallet { >::insert(market_id, court); >::put(jurors); - Self::deposit_event(Event::JurorStakesReassigned { market_id }); + Self::deposit_event(Event::TardyJurorsPunished { market_id }); Ok(()) } @@ -957,14 +959,17 @@ mod pallet { selected } - pub(crate) fn select_jurors(market_id: &MarketIdOf, appeal_number: usize) { - let jurors: JurorPoolOf = JurorPool::::get(); + pub(crate) fn select_jurors( + market_id: &MarketIdOf, + jurors: &[(BalanceOf, T::AccountId)], + appeal_number: usize, + ) { let necessary_jurors_num = Self::necessary_jurors_num(appeal_number); let mut rng = Self::rng(); let actual_len = jurors.len().min(necessary_jurors_num); let random_jurors = - Self::choose_multiple_weighted(market_id, jurors.as_slice(), actual_len, &mut rng); + Self::choose_multiple_weighted(market_id, jurors, actual_len, &mut rng); // we allow at most MaxDrawings jurors // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 @@ -1129,6 +1134,10 @@ mod pallet { ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); + let jurors: JurorPoolOf = JurorPool::::get(); + ensure!(jurors.len() >= INITIAL_JURORS_NUM, Error::::NotEnoughJurors); + Self::select_jurors(market_id, jurors.as_slice(), 0usize); + let now = >::block_number(); let periods = Periods { @@ -1141,8 +1150,6 @@ mod pallet { // sets periods one after the other from now let court = CourtInfo::new(now, periods, T::MaxAppeals::get() as u8); - Self::select_jurors(market_id, 0usize); - let _ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.periods.appeal_end)?; From 73a6a08ebc3bbf11ce51bd926415d03572704ad1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 23 Feb 2023 11:11:15 +0100 Subject: [PATCH 098/368] correct CI --- primitives/src/constants/mock.rs | 2 +- zrml/court/src/tests.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index a08072176..a8da7a9b7 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -27,7 +27,7 @@ parameter_types! { pub const MaxAppeals: u32 = 5; pub const MaxDrawings: u32 = 127; pub const MaxJurors: u32 = 1000; - pub const MinJurorStake: Balance = 5 * BASE; + pub const MinJurorStake: Balance = 50 * CENT; pub const RedistributionPercentage: Percent = Percent::from_percent(50); pub const SlashPercentage: Percent = Percent::from_percent(10); } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index b723fa73f..a7a71051b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -213,6 +213,12 @@ fn random_jurors_returns_a_subset_of_jurors() { fn vote_will_not_accept_unknown_accounts() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); + let amount_alice = 2 * BASE; + let amount_bob = 3 * BASE; + let amount_charlie = 4 * BASE; + Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); + Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); + Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); }); } From eb89baaadb36d17dd0aac04dd1540f5174aa508b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 24 Feb 2023 09:27:45 +0100 Subject: [PATCH 099/368] add voting weight for each juror --- zrml/court/src/lib.rs | 187 ++++++++++++++++++++++------------------ zrml/court/src/types.rs | 16 ++++ 2 files changed, 117 insertions(+), 86 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bd70b9f2e..f1294b1d0 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -40,10 +40,13 @@ pub use types::*; #[frame_support::pallet] mod pallet { use crate::{ - weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CourtStatus, JurorInfo, Periods, - Vote, + weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CourtStatus, Draw, JurorInfo, + Periods, Vote, + }; + use alloc::{ + collections::{BTreeMap, BTreeSet}, + vec::Vec, }; - use alloc::{collections::BTreeMap, vec::Vec}; use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, @@ -56,7 +59,7 @@ mod pallet { transactional, Blake2_128Concat, BoundedVec, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; - use rand::{rngs::StdRng, RngCore, SeedableRng}; + use rand::{rngs::StdRng, Rng, RngCore, SeedableRng}; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup}, DispatchError, Percent, SaturatedConversion, @@ -117,7 +120,7 @@ mod pallet { /// The maximum number of randomly selected jurors for a dispute. #[pallet::constant] - type MaxDrawings: Get; + type MaxDraws: Get; /// The maximum number of jurors that can be registered. #[pallet::constant] @@ -182,10 +185,9 @@ mod pallet { (BalanceOf, ::AccountId), ::MaxJurors, >; - pub(crate) type DrawingsOf = BoundedVec< - (::AccountId, Vote<::Hash>), - ::MaxDrawings, - >; + pub(crate) type DrawOf = + Draw<::AccountId, ::Hash>; + pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -206,8 +208,8 @@ mod pallet { /// The randomly selected jurors with their vote. #[pallet::storage] - pub type Drawings = - StorageMap<_, Blake2_128Concat, MarketIdOf, DrawingsOf, ValueQuery>; + pub type Draws = + StorageMap<_, Blake2_128Concat, MarketIdOf, DrawsOf, ValueQuery>; /// The general information about each court. #[pallet::storage] @@ -447,8 +449,8 @@ mod pallet { ); // ensure not drawn for any market - for (_, drawings) in >::iter() { - ensure!(!drawings.iter().any(|(j, _)| j == &juror), Error::::JurorStillDrawn); + for (_, draws) in >::iter() { + ensure!(!draws.iter().any(|draw| draw.juror == juror), Error::::JurorStillDrawn); } Jurors::::remove(&juror); @@ -469,7 +471,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(n)`, where `n` is the number of jurors - /// in the list of random selections (drawings). + /// in the list of random selections (draws). #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn vote( @@ -486,16 +488,22 @@ mod pallet { Error::::NotInVotingPeriod ); - let mut drawings = >::get(market_id); - match drawings.iter().position(|(juror, _)| juror == &who) { + let mut draws = >::get(market_id); + let (index, weight) = match draws.iter().position(|draw| draw.juror == who) { Some(index) => { - let vote = Vote::Secret { secret: secret_vote }; - drawings[index] = (who.clone(), vote); + ensure!( + matches!(draws[index].vote, Vote::Drawn | Vote::Secret { secret: _ }), + Error::::OnlyDrawnJurorsCanVote + ); + (index, draws[index].weight) } None => return Err(Error::::OnlyDrawnJurorsCanVote.into()), - } + }; + + let vote = Vote::Secret { secret: secret_vote }; + draws[index] = Draw { juror: who.clone(), weight, vote }; - >::insert(market_id, drawings); + >::insert(market_id, draws); Self::deposit_event(Event::JurorVoted { juror: who, market_id, secret: secret_vote }); Ok(()) @@ -517,7 +525,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(n)`, where `n` is the number of jurors - /// in the list of random selections (drawings). + /// in the list of random selections (draws). #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn denounce_vote( @@ -543,9 +551,9 @@ mod pallet { Error::::NotInVotingPeriod ); - let mut drawings = >::get(market_id); - let (index, vote) = match drawings.iter().position(|(j, _)| j == &juror) { - Some(index) => (index, drawings[index].1.clone()), + let mut draws = >::get(market_id); + let (index, weight, vote) = match draws.iter().position(|draw| draw.juror == juror) { + Some(index) => (index, draws[index].weight, draws[index].vote.clone()), None => return Err(Error::::JurorNotDrawn.into()), }; @@ -585,8 +593,8 @@ mod pallet { } let raw_vote = Vote::Denounced { secret, outcome: outcome.clone(), salt }; - drawings[index] = (juror.clone(), raw_vote); - >::insert(market_id, drawings); + draws[index] = Draw { juror: juror.clone(), weight, vote: raw_vote }; + >::insert(market_id, draws); Self::deposit_event(Event::DenouncedJurorVote { denouncer, @@ -610,7 +618,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(n)`, where `n` is the number of jurors - /// in the list of random selections (drawings). + /// in the list of random selections (draws). #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn reveal_vote( @@ -629,9 +637,9 @@ mod pallet { Error::::NotInAggregationPeriod ); - let mut drawings = >::get(market_id); - let (index, vote) = match drawings.iter().position(|(juror, _)| juror == &who) { - Some(index) => (index, drawings[index].1.clone()), + let mut draws = >::get(market_id); + let (index, weight, vote) = match draws.iter().position(|draw| draw.juror == who) { + Some(index) => (index, draws[index].weight, draws[index].vote.clone()), None => return Err(Error::::JurorNotDrawn.into()), }; @@ -658,8 +666,8 @@ mod pallet { }; let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; - drawings[index] = (who.clone(), raw_vote); - >::insert(market_id, drawings); + draws[index] = Draw { juror: who.clone(), weight, vote: raw_vote }; + >::insert(market_id, draws); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); Ok(()) @@ -725,7 +733,7 @@ mod pallet { let jurors: JurorPoolOf = JurorPool::::get(); ensure!(jurors.len() >= INITIAL_JURORS_NUM, Error::::NotEnoughJurors); Self::select_jurors(&market_id, jurors.as_slice(), appeal_number); - // at the time of flushing the last drawings in `select_jurors` + // at the time of flushing the last draws in `select_jurors` // we want to avoid the resolution before the full appeal is executed // otherwise `draw_appeal_jurors` would replace all votes with `Vote::Drawn` // So, the appeal is inevitable after the call to this extrinsic @@ -843,13 +851,15 @@ mod pallet { } }; - for (juror, vote) in Drawings::::get(market_id).iter() { - match vote { + for draw in Draws::::get(market_id).iter() { + match draw.vote { Vote::Drawn => { - slash_and_remove_juror(juror); + // TODO slash according to weight? + slash_and_remove_juror(&draw.juror); } Vote::Secret { secret: _ } => { - slash_and_remove_juror(juror); + // TODO slash according to weight? + slash_and_remove_juror(&draw.juror); } // denounce extrinsic already punished the juror Vote::Denounced { secret: _, outcome: _, salt: _ } => (), @@ -894,13 +904,13 @@ mod pallet { _ => return Err(Error::::CourtNotClosed.into()), }; - let drawings = Drawings::::get(market_id); + let draws = Draws::::get(market_id); - let mut valid_winners_and_losers = Vec::with_capacity(drawings.len()); + let mut valid_winners_and_losers = Vec::with_capacity(draws.len()); - for (juror, vote) in drawings { - if let Vote::Revealed { secret: _, outcome, salt: _ } = vote { - valid_winners_and_losers.push((juror, outcome)); + for draw in draws { + if let Vote::Revealed { secret: _, outcome, salt: _ } = draw.vote { + valid_winners_and_losers.push((draw.juror, outcome)); } } @@ -908,7 +918,7 @@ mod pallet { court.status = CourtStatus::Closed { winner, punished: true, reassigned: true }; >::insert(market_id, court); - >::remove(market_id); + >::remove(market_id); Self::deposit_event(Event::JurorStakesReassigned { market_id }); @@ -921,42 +931,48 @@ mod pallet { T: Config, { pub(crate) fn choose_multiple_weighted( - market_id: &MarketIdOf, jurors: &[(BalanceOf, T::AccountId)], number: usize, rng: &mut R, - ) -> Vec<(T::AccountId, Vote)> { - use rand::{ - distributions::{Distribution, WeightedIndex}, - seq::SliceRandom, - }; - - let mut selected = Vec::with_capacity(number); - - let res = WeightedIndex::new(jurors.iter().map(|item| item.0.saturated_into::())); + ) -> Vec> { + let total_weight = + jurors.iter().map(|(stake, _)| (*stake).saturated_into::()).sum::(); + + let mut random_set = BTreeSet::new(); + for _ in 0..number { + let random_number = rng.gen_range(0u128..total_weight); + random_set.insert(random_number); + } - match res { - Ok(distribution) => { - for _ in 0..number { - selected.push((jurors[distribution.sample(rng)].1.clone(), Vote::Drawn)); + let mut selections = BTreeMap::::new(); + + let mut current_weight = 0u128; + for (stake, juror) in jurors { + let lower_bound = current_weight; + let upper_bound = current_weight.saturating_add((*stake).saturated_into::()); + + // this always gets the lowest random number first and maybe removes it + for random_number in random_set.clone().iter() { + if &lower_bound <= random_number && random_number < &upper_bound { + if let Some(el) = selections.get_mut(juror) { + *el = el.saturating_add(1); + } else { + selections.insert(juror.clone(), 1); + } + random_set.remove(random_number); + } else { + break; } } - Err(err) => { - // this can also happen when there are no jurors - log::warn!( - "Court: weighted selection failed, falling back to random selection for \ - market {:?} with error: {:?}.", - market_id, - err - ); - // fallback to random selection if weighted selection fails - jurors.choose_multiple(rng, number).for_each(|item| { - selected.push((item.1.clone(), Vote::Drawn)); - }); + + if random_set.is_empty() { + break; } + + current_weight = upper_bound; } - selected + selections.into_iter().map(|(juror, weight)| Draw { juror, weight, vote: Vote::Drawn }).collect() } pub(crate) fn select_jurors( @@ -968,15 +984,14 @@ mod pallet { let mut rng = Self::rng(); let actual_len = jurors.len().min(necessary_jurors_num); - let random_jurors = - Self::choose_multiple_weighted(market_id, jurors, actual_len, &mut rng); + let random_jurors = Self::choose_multiple_weighted(jurors, actual_len, &mut rng); - // we allow at most MaxDrawings jurors + // we allow at most MaxDraws jurors // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 - // MaxDrawings should be 127 in this case - let drawings = >::truncate_from(random_jurors); - // new appeal round should have a fresh set of drawings - >::insert(market_id, drawings); + // MaxDraws should be 127 in this case + let draws = >::truncate_from(random_jurors); + // new appeal round should have a fresh set of draws + >::insert(market_id, draws); } pub(crate) fn check_appealable_market( @@ -1081,16 +1096,16 @@ mod pallet { } fn get_winner( - votes: &[(T::AccountId, Vote)], + draws: &[DrawOf], ) -> Result { let mut scores = BTreeMap::::new(); - for (_juror, vote) in votes { - if let Vote::Revealed { secret: _, outcome, salt: _ } = vote { + for draw in draws { + if let Vote::Revealed { secret: _, outcome, salt: _ } = &draw.vote { if let Some(el) = scores.get_mut(outcome) { - *el = el.saturating_add(1); + *el = el.saturating_add(draw.weight); } else { - scores.insert(outcome.clone(), 1); + scores.insert(outcome.clone(), draw.weight); } } } @@ -1100,7 +1115,7 @@ mod pallet { let mut best_score = if let Some(first) = iter.next() { first } else { - // TODO(#980): Create another voting round + // TODO(#980): Create another voting round or trigger global dispute return Err(Error::::NoVotes.into()); }; @@ -1169,8 +1184,8 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let drawings = Drawings::::get(market_id); - let winner_outcome = Self::get_winner(drawings.as_slice())?; + let draws = Draws::::get(market_id); + let winner_outcome = Self::get_winner(draws.as_slice())?; court.status = CourtStatus::Closed { winner: winner_outcome.clone(), @@ -1235,7 +1250,7 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - >::remove(market_id); + >::remove(market_id); >::remove(market_id); Ok(()) } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 80764e234..aae8b465b 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -141,3 +141,19 @@ impl CourtInfo self.periods.appeal_end = self.periods.aggregation_end.saturating_add(periods.appeal_end); } } + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct Draw { + pub(crate) juror: AccountId, + pub(crate) weight: u32, + pub(crate) vote: Vote, +} From 840975f7042b74a568509a8dc0040777c140405d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 24 Feb 2023 10:50:19 +0100 Subject: [PATCH 100/368] reserve appeal bond --- zrml/court/src/lib.rs | 107 +++++++++++++++++++++++----------------- zrml/court/src/types.rs | 43 ++++++++-------- 2 files changed, 84 insertions(+), 66 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index f1294b1d0..e5327466c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -40,8 +40,8 @@ pub use types::*; #[frame_support::pallet] mod pallet { use crate::{ - weights::WeightInfoZeitgeist, CourtInfo, CourtPalletApi, CourtStatus, Draw, JurorInfo, - Periods, Vote, + weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, + JurorInfo, Periods, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -51,7 +51,7 @@ mod pallet { use frame_support::{ dispatch::DispatchResult, ensure, log, - pallet_prelude::{EnsureOrigin, OptionQuery, StorageMap, StorageValue, ValueQuery}, + pallet_prelude::{OptionQuery, StorageMap, StorageValue, ValueQuery}, traits::{ BalanceStatus, Currency, Get, Imbalance, IsType, NamedReservableCurrency, Randomness, StorageVersion, @@ -72,8 +72,14 @@ mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - /// The origin which may start appeals. - type AppealOrigin: EnsureOrigin; + /// The required base bond in order to get an appeal initiated. + #[pallet::constant] + type AppealBond: Get>; + + /// The additional amount of currency that must be bonded when creating a subsequent + /// appeal. + #[pallet::constant] + type AppealBondFactor: Get>; /// The time to wait before jurors can start voting. /// The intention is to use this period as preparation time @@ -179,7 +185,7 @@ mod pallet { >; pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - pub(crate) type CourtOf = CourtInfo<::BlockNumber>; + pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; pub(crate) type JurorInfoOf = JurorInfo>; pub(crate) type JurorPoolOf = BoundedVec< (BalanceOf, ::AccountId), @@ -188,6 +194,8 @@ mod pallet { pub(crate) type DrawOf = Draw<::AccountId, ::Hash>; pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; + pub(crate) type AppealOf = AppealInfo<::AccountId, BalanceOf>; + pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -248,9 +256,9 @@ mod pallet { /// The jurors for an appeal have been drawn. AppealJurorsDrawn { market_id: MarketIdOf }, /// The backing for an appeal has been checked. - AppealBackingChecked { market_id: MarketIdOf }, + AppealBacked { market_id: MarketIdOf }, /// A market has been appealed. - MarketAppealed { market_id: MarketIdOf, appeal_number: u8 }, + MarketAppealed { market_id: MarketIdOf, appeal_number: u32 }, /// The juror stakes have been reassigned. JurorStakesReassigned { market_id: MarketIdOf }, /// The tardy jurors have been punished. @@ -290,7 +298,7 @@ mod pallet { /// For this appeal round the backing check extrinsic was already called. AppealAlreadyBacked, /// In order to start an appeal the backing check extrinsic must be called first. - CheckBackingFirst, + BackAppealFirst, /// The final appeal extrinsic can only be called after the backing check extrinsic /// and random selection of jurors for this appeal. AppealNotReady, @@ -673,7 +681,7 @@ mod pallet { Ok(()) } - /// Check if the appeal of a court is allowed to get initiated. + /// Back an appeal of a court to get an appeal initiated. /// /// # Arguments /// @@ -684,21 +692,34 @@ mod pallet { /// Complexity: `O(1)` #[pallet::weight(1_000_000_000_000)] #[transactional] - pub fn check_appeal_backing( - origin: OriginFor, - market_id: MarketIdOf, - ) -> DispatchResult { - T::AppealOrigin::ensure_origin(origin)?; + pub fn back_appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + let who = ensure_signed(origin)?; let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - ensure!(!court.appeal_info.is_backed, Error::::AppealAlreadyBacked); + ensure!(!court.is_appeal_backed, Error::::AppealAlreadyBacked); + let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - court.appeal_info.is_backed = true; + let appeal_number = court.appeals.len(); + let bond = default_appeal_bond::(appeal_number); + let appeal_info = AppealInfo { backer: who.clone(), bond }; + + court.appeals.try_push(appeal_info).map_err(|_| Error::::MaxAppealsReached)?; + + CurrencyOf::::reserve_named(&Self::reserve_id(), &who, bond)?; + + let last_resolve_at = court.periods.appeal_end; + + court.is_appeal_backed = true; >::insert(market_id, court); - Self::deposit_event(Event::AppealBackingChecked { market_id }); + // we want to avoid the resolution before the full appeal is executed + // So, the appeal is inevitable after the call to this extrinsic + // otherwise the market is not going to resolve + let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + + Self::deposit_event(Event::AppealBacked { market_id }); Ok(()) } @@ -722,25 +743,18 @@ mod pallet { ensure_signed(origin)?; let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - ensure!(!court.appeal_info.is_drawn, Error::::JurorsAlreadyDrawn); - ensure!(court.appeal_info.is_backed, Error::::CheckBackingFirst); + ensure!(!court.is_drawn, Error::::JurorsAlreadyDrawn); + ensure!(court.is_appeal_backed, Error::::BackAppealFirst); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - let last_resolve_at = court.periods.appeal_end; - - let appeal_number = court.appeal_info.current as usize; + let appeal_number = court.appeals.len().saturating_add(1usize); let jurors: JurorPoolOf = JurorPool::::get(); + // TODO trigger global disputes ensure!(jurors.len() >= INITIAL_JURORS_NUM, Error::::NotEnoughJurors); Self::select_jurors(&market_id, jurors.as_slice(), appeal_number); - // at the time of flushing the last draws in `select_jurors` - // we want to avoid the resolution before the full appeal is executed - // otherwise `draw_appeal_jurors` would replace all votes with `Vote::Drawn` - // So, the appeal is inevitable after the call to this extrinsic - // otherwise the market is not going to resolve - let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); - court.appeal_info.is_drawn = true; + court.is_drawn = true; >::insert(market_id, court); Self::deposit_event(Event::AppealJurorsDrawn { market_id }); @@ -764,7 +778,7 @@ mod pallet { ensure_signed(origin)?; let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - ensure!(court.appeal_info.is_appeal_ready(), Error::::AppealNotReady); + ensure!(court.is_appeal_backed && court.is_drawn, Error::::AppealNotReady); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; @@ -776,9 +790,7 @@ mod pallet { }; // sets periods one after the other from now court.update_periods(periods, now); - let appeal_number = court.appeal_info.current; - court.appeal_info.current = court.appeal_info.current.saturating_add(1); - + let appeal_number = court.appeals.len() as u32; let _ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; @@ -910,6 +922,7 @@ mod pallet { for draw in draws { if let Vote::Revealed { secret: _, outcome, salt: _ } = draw.vote { + // TODO slash and reward according to voting weight! valid_winners_and_losers.push((draw.juror, outcome)); } } @@ -972,7 +985,10 @@ mod pallet { current_weight = upper_bound; } - selections.into_iter().map(|(juror, weight)| Draw { juror, weight, vote: Vote::Drawn }).collect() + selections + .into_iter() + .map(|(juror, weight)| Draw { juror, weight, vote: Vote::Drawn }) + .collect() } pub(crate) fn select_jurors( @@ -1011,11 +1027,6 @@ mod pallet { Error::::NotInAppealPeriod ); - ensure!( - court.appeal_info.current < court.appeal_info.max, - Error::::MaxAppealsReached - ); - Ok(()) } @@ -1095,9 +1106,7 @@ mod pallet { Ok(()) } - fn get_winner( - draws: &[DrawOf], - ) -> Result { + fn get_winner(draws: &[DrawOf]) -> Result { let mut scores = BTreeMap::::new(); for draw in draws { @@ -1163,7 +1172,7 @@ mod pallet { }; // sets periods one after the other from now - let court = CourtInfo::new(now, periods, T::MaxAppeals::get() as u8); + let court = CourtInfo::new(now, periods); let _ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.periods.appeal_end)?; @@ -1257,4 +1266,14 @@ mod pallet { } impl CourtPalletApi for Pallet where T: Config {} + + // No-one can bound more than BalanceOf, therefore, this functions saturates + pub fn default_appeal_bond(n: usize) -> BalanceOf + where + T: Config, + { + T::AppealBond::get().saturating_add( + T::AppealBondFactor::get().saturating_mul(n.saturated_into::().into()), + ) + } } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index aae8b465b..e95489633 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -75,17 +75,9 @@ pub struct Periods { PartialEq, Eq, )] -pub struct AppealInfo { - pub(crate) current: u8, - pub(crate) max: u8, - pub(crate) is_drawn: bool, - pub(crate) is_backed: bool, -} - -impl AppealInfo { - pub fn is_appeal_ready(&self) -> bool { - self.is_drawn && self.is_backed - } +pub enum CourtStatus { + Open, + Closed { winner: OutcomeReport, punished: bool, reassigned: bool }, } #[derive( @@ -98,9 +90,9 @@ impl AppealInfo { PartialEq, Eq, )] -pub enum CourtStatus { - Open, - Closed { winner: OutcomeReport, punished: bool, reassigned: bool }, +pub struct AppealInfo { + pub(crate) backer: AccountId, + pub(crate) bond: Balance, } #[derive( @@ -113,24 +105,31 @@ pub enum CourtStatus { PartialEq, Eq, )] -pub struct CourtInfo { +pub struct CourtInfo { pub(crate) status: CourtStatus, - pub(crate) appeal_info: AppealInfo, + pub(crate) is_appeal_backed: bool, + pub(crate) is_drawn: bool, + pub(crate) appeals: Appeals, pub(crate) periods: Periods, } -impl CourtInfo { - pub fn new(now: BlockNumber, periods: Periods, max_appeals: u8) -> Self { +impl + CourtInfo +{ + pub fn new(now: BlockNumber, periods: Periods) -> Self { let backing_end = now.saturating_add(periods.backing_end); let vote_end = backing_end.saturating_add(periods.vote_end); let aggregation_end = vote_end.saturating_add(periods.aggregation_end); let appeal_end = aggregation_end.saturating_add(periods.appeal_end); let periods = Periods { backing_end, vote_end, aggregation_end, appeal_end }; - // 2^1 * 3 + 2^1 - 1 = 7 jurors in the first appeal round (`necessary_jurors_num`) - let appeal_info = - AppealInfo { current: 1, max: max_appeals, is_drawn: false, is_backed: false }; let status = CourtStatus::Open; - Self { status, appeal_info, periods } + Self { + status, + is_appeal_backed: false, + is_drawn: false, + appeals: Default::default(), + periods, + } } pub fn update_periods(&mut self, periods: Periods, now: BlockNumber) { From 80a983a96b6082e1d359610e5de71f5a65b1b25b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 24 Feb 2023 15:27:41 +0100 Subject: [PATCH 101/368] improve appeal system --- zrml/court/src/lib.rs | 146 ++++++++++++++++++++++++++++++---------- zrml/court/src/types.rs | 1 + 2 files changed, 111 insertions(+), 36 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index e5327466c..d181925f6 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -53,8 +53,8 @@ mod pallet { ensure, log, pallet_prelude::{OptionQuery, StorageMap, StorageValue, ValueQuery}, traits::{ - BalanceStatus, Currency, Get, Imbalance, IsType, NamedReservableCurrency, Randomness, - StorageVersion, + BalanceStatus, Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, + NamedReservableCurrency, Randomness, StorageVersion, WithdrawReasons, }, transactional, Blake2_128Concat, BoundedVec, PalletId, }; @@ -99,6 +99,15 @@ mod pallet { #[pallet::constant] type CourtAppealPeriod: Get; + /// The court lock identifier. + #[pallet::constant] + type CourtLockId: Get; + + /// The currency implementation used to transfer, lock and reserve tokens. + type Currency: Currency + + NamedReservableCurrency + + LockableCurrency; + /// The slash percentage if a secret vote gets revealed during the voting period. #[pallet::constant] type DenounceSlashPercentage: Get; @@ -169,8 +178,7 @@ mod pallet { pub(crate) type BalanceOf = as Currency<::AccountId>>::Balance; - pub(crate) type CurrencyOf = - <::MarketCommons as MarketCommonsPalletApi>::Currency; + pub(crate) type CurrencyOf = ::Currency; pub(crate) type NegativeImbalanceOf = as Currency<::AccountId>>::NegativeImbalance; pub(crate) type MarketIdOf = @@ -338,6 +346,8 @@ mod pallet { PunishTardyJurorsFirst, /// There are not enough jurors in the pool. NotEnoughJurors, + /// The report of the market was not found. + MarketReportNotFound, } #[pallet::call] @@ -387,8 +397,12 @@ mod pallet { .map_err(|_| Error::::MaxJurorsReached)?, }; - // full reserve = prev_juror_info.stake (already reserved) + amount - CurrencyOf::::reserve_named(&Self::reserve_id(), &who, amount)?; + T::Currency::set_lock( + T::CourtLockId::get(), + &who, + juror_info.stake, + WithdrawReasons::all(), + ); JurorPool::::put(jurors); @@ -463,7 +477,7 @@ mod pallet { Jurors::::remove(&juror); - CurrencyOf::::unreserve_all_named(&Self::reserve_id(), &juror); + T::Currency::remove_lock(T::CourtLockId::get(), &juror); Self::deposit_event(Event::ExitedJuror { juror }); Ok(()) @@ -583,9 +597,9 @@ mod pallet { }; let treasury_account_id = Self::treasury_account_id(); - let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), &juror); + let all_reserved = T::Currency::reserved_balance_named(&Self::reserve_id(), &juror); let slash = T::DenounceSlashPercentage::get() * all_reserved; - let _ = CurrencyOf::::repatriate_reserved_named( + let _ = T::Currency::repatriate_reserved_named( &Self::reserve_id(), &juror, &treasury_account_id, @@ -701,16 +715,20 @@ mod pallet { let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; + let draws = Draws::::get(market_id); + // TODO: shouldn't fail here, especially when there are no votes an appeal should be necessary or start global dispute + let appealed_outcome = Self::get_winner(draws.as_slice())?; + let appeal_number = court.appeals.len(); let bond = default_appeal_bond::(appeal_number); - let appeal_info = AppealInfo { backer: who.clone(), bond }; + let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; court.appeals.try_push(appeal_info).map_err(|_| Error::::MaxAppealsReached)?; - CurrencyOf::::reserve_named(&Self::reserve_id(), &who, bond)?; + T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; let last_resolve_at = court.periods.appeal_end; - + court.is_appeal_backed = true; >::insert(market_id, court); @@ -748,11 +766,9 @@ mod pallet { let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - let appeal_number = court.appeals.len().saturating_add(1usize); let jurors: JurorPoolOf = JurorPool::::get(); - // TODO trigger global disputes - ensure!(jurors.len() >= INITIAL_JURORS_NUM, Error::::NotEnoughJurors); - Self::select_jurors(&market_id, jurors.as_slice(), appeal_number); + let appeal_number = court.appeals.len(); + Self::select_jurors(&market_id, jurors.as_slice(), appeal_number)?; court.is_drawn = true; >::insert(market_id, court); @@ -831,9 +847,9 @@ mod pallet { let mut jurors = JurorPool::::get(); let mut slash_and_remove_juror = |ai: &T::AccountId| { - let all_reserved = CurrencyOf::::reserved_balance_named(&Self::reserve_id(), ai); + let all_reserved = T::Currency::reserved_balance_named(&Self::reserve_id(), ai); let slash = T::SlashPercentage::get() * all_reserved; - let res = CurrencyOf::::repatriate_reserved_named( + let res = T::Currency::repatriate_reserved_named( &Self::reserve_id(), ai, &treasury_account_id, @@ -995,12 +1011,14 @@ mod pallet { market_id: &MarketIdOf, jurors: &[(BalanceOf, T::AccountId)], appeal_number: usize, - ) { - let necessary_jurors_num = Self::necessary_jurors_num(appeal_number); + ) -> Result<(), DispatchError> { + let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); + ensure!(jurors.len() >= necessary_jurors_number, Error::::NotEnoughJurors); + let mut rng = Self::rng(); - let actual_len = jurors.len().min(necessary_jurors_num); - let random_jurors = Self::choose_multiple_weighted(jurors, actual_len, &mut rng); + let random_jurors = + Self::choose_multiple_weighted(jurors, necessary_jurors_number, &mut rng); // we allow at most MaxDraws jurors // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 @@ -1008,6 +1026,8 @@ mod pallet { let draws = >::truncate_from(random_jurors); // new appeal round should have a fresh set of draws >::insert(market_id, draws); + + Ok(()) } pub(crate) fn check_appealable_market( @@ -1081,10 +1101,10 @@ mod pallet { winners.push(juror); } else { let all_reserved = - CurrencyOf::::reserved_balance_named(&Self::reserve_id(), juror); + T::Currency::reserved_balance_named(&Self::reserve_id(), juror); let slash = T::RedistributionPercentage::get() * all_reserved; let (imb, _excess) = - CurrencyOf::::slash_reserved_named(&Self::reserve_id(), juror, slash); + T::Currency::slash_reserved_named(&Self::reserve_id(), juror, slash); total_incentives.subsume(imb); } } @@ -1095,12 +1115,12 @@ mod pallet { for juror in winners { let (actual_reward, leftover) = total_incentives.split(reward_per_each); total_incentives = leftover; - CurrencyOf::::resolve_creating(juror, actual_reward); + T::Currency::resolve_creating(juror, actual_reward); } } else { // if there are no winners reward the treasury let treasury_acc = Self::treasury_account_id(); - CurrencyOf::::resolve_creating(&treasury_acc, total_incentives); + T::Currency::resolve_creating(&treasury_acc, total_incentives); } Ok(()) @@ -1159,8 +1179,8 @@ mod pallet { ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); let jurors: JurorPoolOf = JurorPool::::get(); - ensure!(jurors.len() >= INITIAL_JURORS_NUM, Error::::NotEnoughJurors); - Self::select_jurors(market_id, jurors.as_slice(), 0usize); + let appeal_number = 0usize; + Self::select_jurors(market_id, jurors.as_slice(), appeal_number)?; let now = >::block_number(); @@ -1207,16 +1227,27 @@ mod pallet { } fn maybe_pay( - _: &Self::MarketId, + market_id: &Self::MarketId, market: &MarketOf, - _: &OutcomeReport, - overall_imbalance: NegativeImbalanceOf, + resolved_outcome: &OutcomeReport, + mut overall_imbalance: NegativeImbalanceOf, ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - // TODO all funds to treasury? + + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + for AppealInfo { backer, bond, appealed_outcome } in &court.appeals { + if resolved_outcome == appealed_outcome { + let (imb, _) = + T::Currency::slash_reserved_named(&Self::reserve_id(), backer, *bond); + overall_imbalance.subsume(imb); + } else { + T::Currency::unreserve_named(&Self::reserve_id(), backer, *bond); + } + } + Ok(overall_imbalance) } @@ -1234,7 +1265,7 @@ mod pallet { } fn has_failed( - _market_id: &Self::MarketId, + market_id: &Self::MarketId, market: &MarketOf, ) -> Result { ensure!( @@ -1242,15 +1273,58 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - // TODO: for now disallow global dispute for court, later use max appeals check - Ok(false) + let mut has_failed = false; + let now = >::block_number(); + + let jurors: JurorPoolOf = JurorPool::::get(); + match >::get(market_id) { + Some(court) => { + let appeals = &court.appeals; + let appeal_number = appeals.len(); + let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); + let valid_period = + Self::check_appealable_market(market_id, &court, now).is_ok(); + + if valid_period { + if jurors.len() < necessary_jurors_number { + has_failed = true; + } + + if appeals.is_full() { + has_failed = true; + } + } + } + None => { + let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; + let report_block = report.at; + let block_after_dispute_duration = + report_block.saturating_add(market.deadlines.dispute_duration); + let during_dispute_duration = + report_block <= now && now < block_after_dispute_duration; + + let necessary_jurors_number = Self::necessary_jurors_num(0usize); + if jurors.len() < necessary_jurors_number && during_dispute_duration { + has_failed = true; + } + } + } + + Ok(has_failed) } - fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_global_dispute(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); + + if let Some(court) = >::get(market_id) { + let last_resolve_at = court.periods.appeal_end; + let _ids_len_0 = + T::DisputeResolution::remove_auto_resolve(market_id, last_resolve_at); + } + Ok(()) } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index e95489633..3933a4edd 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -93,6 +93,7 @@ pub enum CourtStatus { pub struct AppealInfo { pub(crate) backer: AccountId, pub(crate) bond: Balance, + pub(crate) appealed_outcome: OutcomeReport, } #[derive( From 74cf9d29e84ba1eef9eddd49eb60fda610a1a6bf Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Feb 2023 09:03:18 +0100 Subject: [PATCH 102/368] use min_stake for slash --- primitives/src/constants/mock.rs | 2 +- runtime/battery-station/src/parameters.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/court/src/lib.rs | 126 ++++++++++++---------- 4 files changed, 71 insertions(+), 61 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index a8da7a9b7..20d30a5fc 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -25,7 +25,7 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = 34; pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); pub const MaxAppeals: u32 = 5; - pub const MaxDrawings: u32 = 127; + pub const MaxDrawings: u32 = 191; pub const MaxJurors: u32 = 1000; pub const MinJurorStake: Balance = 50 * CENT; pub const RedistributionPercentage: Percent = Percent::from_percent(50); diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 3c20d5f8b..aa7deb068 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -96,7 +96,7 @@ parameter_types! { /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDrawings: u32 = 127; + pub const MaxDrawings: u32 = 191; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 46594abc0..cd9ca261d 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -96,7 +96,7 @@ parameter_types! { /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDrawings: u32 = 127; + pub const MaxDrawings: u32 = 191; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d181925f6..1d20a54ed 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -53,7 +53,7 @@ mod pallet { ensure, log, pallet_prelude::{OptionQuery, StorageMap, StorageValue, ValueQuery}, traits::{ - BalanceStatus, Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, + Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, NamedReservableCurrency, Randomness, StorageVersion, WithdrawReasons, }, transactional, Blake2_128Concat, BoundedVec, PalletId, @@ -61,7 +61,7 @@ mod pallet { use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use rand::{rngs::StdRng, Rng, RngCore, SeedableRng}; use sp_runtime::{ - traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup}, + traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup, Zero}, DispatchError, Percent, SaturatedConversion, }; use zeitgeist_primitives::{ @@ -103,6 +103,10 @@ mod pallet { #[pallet::constant] type CourtLockId: Get; + /// Identifier of this pallet + #[pallet::constant] + type CourtPalletId: Get; + /// The currency implementation used to transfer, lock and reserve tokens. type Currency: Currency + NamedReservableCurrency @@ -145,10 +149,6 @@ mod pallet { #[pallet::constant] type MinJurorStake: Get>; - /// Identifier of this pallet - #[pallet::constant] - type PalletId: Get; - /// Randomness source type Random: Randomness; @@ -156,9 +156,9 @@ mod pallet { #[pallet::constant] type RedistributionPercentage: Get; - /// The percentage that is being slashed from the juror's stake. + /// The percentage that is being slashed from the juror's stake in case she is tardy. #[pallet::constant] - type SlashPercentage: Get; + type TardySlashPercentage: Get; /// Slashed funds are send to the treasury #[pallet::constant] @@ -169,7 +169,7 @@ mod pallet { } // Number of jurors for an initial market dispute - const INITIAL_JURORS_NUM: usize = 3; + const INITIAL_JURORS_NUM: usize = 5; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); // Weight used to increase the number of jurors for subsequent appeals @@ -596,16 +596,13 @@ mod pallet { } }; - let treasury_account_id = Self::treasury_account_id(); - let all_reserved = T::Currency::reserved_balance_named(&Self::reserve_id(), &juror); - let slash = T::DenounceSlashPercentage::get() * all_reserved; - let _ = T::Currency::repatriate_reserved_named( - &Self::reserve_id(), - &juror, - &treasury_account_id, - slash, - BalanceStatus::Free, - )?; + let reward_pot = Self::reward_pot(&market_id); + let free_balance = T::Currency::free_balance(&juror); + let slash = T::DenounceSlashPercentage::get() * T::MinJurorStake::get(); + let slash = slash.min(free_balance); + let (imbalance, missing) = T::Currency::slash(&juror, slash); + debug_assert!(missing.is_zero(), "Could not slash all of the amount."); + T::Currency::resolve_creating(&reward_pot, imbalance); let mut jurors = JurorPool::::get(); if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { @@ -843,28 +840,19 @@ mod pallet { _ => return Err(Error::::CourtNotClosed.into()), }; - let treasury_account_id = Self::treasury_account_id(); - let mut jurors = JurorPool::::get(); let mut slash_and_remove_juror = |ai: &T::AccountId| { - let all_reserved = T::Currency::reserved_balance_named(&Self::reserve_id(), ai); - let slash = T::SlashPercentage::get() * all_reserved; - let res = T::Currency::repatriate_reserved_named( - &Self::reserve_id(), - ai, - &treasury_account_id, - slash, - BalanceStatus::Free, + let all_free = T::Currency::free_balance(ai); + let slash = T::TardySlashPercentage::get() * T::MinJurorStake::get(); + let slash = slash.min(all_free); + let (imbalance, missing) = T::Currency::slash(ai, slash); + debug_assert!( + missing.is_zero(), + "Could not slash all of the amount for juror {:?}.", + ai ); - if let Err(e) = res { - log::warn!( - "Failed to slash juror {:?} for market {:?}: {:?}", - ai, - market_id, - e - ); - debug_assert!(false); - } + let reward_pot = Self::reward_pot(&market_id); + T::Currency::resolve_creating(&reward_pot, imbalance); if let Some(prev_juror_info) = >::get(ai) { if let Ok(i) = @@ -943,7 +931,11 @@ mod pallet { } } - Self::slash_losers_to_award_winners(valid_winners_and_losers.as_slice(), &winner)?; + Self::slash_losers_to_award_winners( + &market_id, + valid_winners_and_losers.as_slice(), + &winner, + )?; court.status = CourtStatus::Closed { winner, punished: true, reassigned: true }; >::insert(market_id, court); @@ -1021,8 +1013,8 @@ mod pallet { Self::choose_multiple_weighted(jurors, necessary_jurors_number, &mut rng); // we allow at most MaxDraws jurors - // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 - // MaxDraws should be 127 in this case + // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 + // MaxDraws should be 191 in this case let draws = >::truncate_from(random_jurors); // new appeal round should have a fresh set of draws >::insert(market_id, draws); @@ -1053,7 +1045,17 @@ mod pallet { /// The reserve ID of the court pallet. #[inline] pub fn reserve_id() -> [u8; 8] { - T::PalletId::get().0 + T::CourtPalletId::get().0 + } + + #[inline] + pub fn reward_pot(market_id: &MarketIdOf) -> T::AccountId { + T::CourtPalletId::get().into_sub_account_truncating(market_id) + } + + #[inline] + pub(crate) fn treasury_account_id() -> T::AccountId { + T::TreasuryPalletId::get().into_account_truncating() } // Returns a pseudo random number generator implementation based on the seed @@ -1072,15 +1074,10 @@ mod pallet { StdRng::from_seed(seed) } - #[inline] - pub(crate) fn treasury_account_id() -> T::AccountId { - T::TreasuryPalletId::get().into_account_truncating() - } - // Calculates the necessary number of jurors depending on the number of market appeals. fn necessary_jurors_num(appeals_len: usize) -> usize { - // 2^(appeals_len) * 3 + 2^(appeals_len) - 1 - // MaxAppeals (= 5) example: 2^5 * 3 + 2^5 - 1 = 127 + // 2^(appeals_len) * 5 + 2^(appeals_len) - 1 + // MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 SUBSEQUENT_JURORS_FACTOR .saturating_pow(appeals_len as u32) .saturating_mul(INITIAL_JURORS_NUM) @@ -1090,6 +1087,7 @@ mod pallet { } fn slash_losers_to_award_winners( + market_id: &MarketIdOf, valid_winners_and_losers: &[(T::AccountId, OutcomeReport)], winner_outcome: &OutcomeReport, ) -> DispatchResult { @@ -1100,15 +1098,25 @@ mod pallet { if outcome == winner_outcome { winners.push(juror); } else { - let all_reserved = - T::Currency::reserved_balance_named(&Self::reserve_id(), juror); - let slash = T::RedistributionPercentage::get() * all_reserved; - let (imb, _excess) = - T::Currency::slash_reserved_named(&Self::reserve_id(), juror, slash); + let free_balance = T::Currency::free_balance(&juror); + let slash = T::RedistributionPercentage::get() * T::MinJurorStake::get(); + let slash = slash.min(free_balance); + let (imb, missing) = T::Currency::slash(&juror, slash); + debug_assert!( + missing.is_zero(), + "Could not slash all of the amount for juror {:?}.", + juror + ); total_incentives.subsume(imb); } } + let reward_pot = Self::reward_pot(market_id); + let reward = T::Currency::free_balance(&reward_pot); + let (imb, missing) = T::Currency::slash(&reward_pot, reward); + debug_assert!(missing.is_zero(), "Could not slash all of the amount for reward pot."); + total_incentives.subsume(imb); + if let Some(reward_per_each) = total_incentives.peek().checked_div(&winners.len().saturated_into()) { @@ -1238,13 +1246,13 @@ mod pallet { ); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - for AppealInfo { backer, bond, appealed_outcome } in &court.appeals { - if resolved_outcome == appealed_outcome { + for AppealInfo { backer, bond, appealed_outcome } in court.appeals { + if resolved_outcome == &appealed_outcome { let (imb, _) = - T::Currency::slash_reserved_named(&Self::reserve_id(), backer, *bond); + T::Currency::slash_reserved_named(&Self::reserve_id(), &backer, bond); overall_imbalance.subsume(imb); } else { - T::Currency::unreserve_named(&Self::reserve_id(), backer, *bond); + T::Currency::unreserve_named(&Self::reserve_id(), &backer, bond); } } @@ -1276,6 +1284,8 @@ mod pallet { let mut has_failed = false; let now = >::block_number(); + // TODO maybe add the case that the voting decision is unclear (or NoVotes case) + let jurors: JurorPoolOf = JurorPool::::get(); match >::get(market_id) { Some(court) => { From 3c010ac11ec247036cda85c884b1c3833c8be7e6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Feb 2023 13:10:59 +0100 Subject: [PATCH 103/368] draw without replacement --- primitives/src/constants/mock.rs | 6 +- runtime/battery-station/src/parameters.rs | 8 +- runtime/common/src/lib.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 8 +- zrml/court/src/lib.rs | 216 ++++++++++++++-------- zrml/court/src/mock.rs | 4 +- zrml/court/src/types.rs | 19 +- zrml/prediction-markets/src/mock.rs | 4 +- 8 files changed, 172 insertions(+), 95 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 20d30a5fc..2ebfab425 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -23,13 +23,13 @@ parameter_types! { pub const CourtVotePeriod: BlockNumber = 34; pub const CourtAggregationPeriod: BlockNumber = 34; pub const CourtAppealPeriod: BlockNumber = 34; - pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); + pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); pub const MaxAppeals: u32 = 5; pub const MaxDrawings: u32 = 191; pub const MaxJurors: u32 = 1000; pub const MinJurorStake: Balance = 50 * CENT; - pub const RedistributionPercentage: Percent = Percent::from_percent(50); - pub const SlashPercentage: Percent = Percent::from_percent(10); + pub const RedistributionPercentage: Percent = Percent::from_percent(10); + pub const TardySlashPercentage: Percent = Percent::from_percent(20); } // Global disputes parameters diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index aa7deb068..f6a4372b5 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -92,7 +92,7 @@ parameter_types! { /// The time in which a court case can get appealed. pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The slash percentage if a secret vote gets revealed during the voting period. - pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); + pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. @@ -100,11 +100,11 @@ parameter_types! { /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. - pub const MinJurorStake: Balance = 5 * BASE; + pub const MinJurorStake: Balance = 1000 * BASE; /// The percentage that is slashed if a juror did not vote for the plurality outcome. - pub const RedistributionPercentage: Percent = Percent::from_percent(50); + pub const RedistributionPercentage: Percent = Percent::from_percent(10); /// The percentage that is being slashed from the juror's stake. - pub const SlashPercentage: Percent = Percent::from_percent(10); + pub const TardySlashPercentage: Percent = Percent::from_percent(20); // Democracy /// How often (in blocks) new public referenda are launched. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index db8fc9c88..003ab0815 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -954,7 +954,7 @@ macro_rules! impl_config_traits { type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; - type SlashPercentage = SlashPercentage; + type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index cd9ca261d..27ff1d879 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -92,7 +92,7 @@ parameter_types! { /// The time in which a court case can get appealed. pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The slash percentage if a secret vote gets revealed during the voting period. - pub const DenounceSlashPercentage: Percent = Percent::from_percent(70); + pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. @@ -100,11 +100,11 @@ parameter_types! { /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. - pub const MinJurorStake: Balance = 2 * BASE; + pub const MinJurorStake: Balance = 1000 * BASE; /// The percentage that is slashed if a juror did not vote for the plurality outcome. - pub const RedistributionPercentage: Percent = Percent::from_percent(50); + pub const RedistributionPercentage: Percent = Percent::from_percent(10); /// The percentage that is being slashed from the juror's stake. - pub const SlashPercentage: Percent = Percent::from_percent(10); + pub const TardySlashPercentage: Percent = Percent::from_percent(20); // Democracy /// How often (in blocks) new public referenda are launched. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 1d20a54ed..7e53b9702 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -41,7 +41,7 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, - JurorInfo, Periods, Vote, + JurorInfo, JurorPoolItem, Periods, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -195,12 +195,14 @@ mod pallet { <::Lookup as StaticLookup>::Source; pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; pub(crate) type JurorInfoOf = JurorInfo>; - pub(crate) type JurorPoolOf = BoundedVec< - (BalanceOf, ::AccountId), - ::MaxJurors, + pub(crate) type JurorPoolItemOf = + JurorPoolItem<::AccountId, BalanceOf>; + pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; + pub(crate) type DrawOf = Draw< + ::AccountId, + BalanceOf, + ::Hash, >; - pub(crate) type DrawOf = - Draw<::AccountId, ::Hash>; pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; pub(crate) type AppealOf = AppealInfo<::AccountId, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; @@ -348,6 +350,10 @@ mod pallet { NotEnoughJurors, /// The report of the market was not found. MarketReportNotFound, + /// The caller has not enough funds to join the court with the specified amount. + InsufficientAmount, + /// After the first join of the court the amount has to be higher than the last join. + AmountBelowLastJoin, } #[pallet::call] @@ -355,6 +361,7 @@ mod pallet { /// Join to become a juror, who is able to get randomly selected /// for court cases according to the provided stake. /// The probability to get selected is higher the more funds are staked. + /// The amount is added to the stake-weighted pool. /// /// # Arguments /// @@ -368,13 +375,15 @@ mod pallet { pub fn join_court(origin: OriginFor, amount: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); + let free_balance = T::Currency::free_balance(&who); + ensure!(amount <= free_balance, Error::::InsufficientAmount); let mut jurors = JurorPool::::get(); - let mut juror_info = JurorInfoOf:: { stake: amount }; - if let Some(prev_juror_info) = >::get(&who) { - if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); + if let Ok(i) = + jurors.binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) { jurors.remove(i); } else { @@ -383,29 +392,30 @@ mod pallet { // or if `prepare_exit_court` was called return Err(Error::::JurorNeedsToExit.into()); } - - juror_info.stake = juror_info.stake.saturating_add(prev_juror_info.stake); } - match jurors.binary_search_by_key(&juror_info.stake, |tuple| tuple.0) { + match jurors.binary_search_by_key(&amount, |pool_item| pool_item.stake) { // The reason for this error is that each amount has a clear juror // binary_search_by_key could otherwise return an index of an unwanted juror // if there are multiple jurors with the same stake Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), Err(i) => jurors - .try_insert(i, (juror_info.stake, who.clone())) + .try_insert( + i, + JurorPoolItem { + stake: amount, + juror: who.clone(), + slashed: >::zero(), + }, + ) .map_err(|_| Error::::MaxJurorsReached)?, }; - T::Currency::set_lock( - T::CourtLockId::get(), - &who, - juror_info.stake, - WithdrawReasons::all(), - ); + T::Currency::set_lock(T::CourtLockId::get(), &who, amount, WithdrawReasons::all()); JurorPool::::put(jurors); + let juror_info = JurorInfoOf:: { stake: amount }; >::insert(&who, juror_info); Self::deposit_event(Event::JoinedJuror { juror: who }); @@ -427,7 +437,9 @@ mod pallet { let mut jurors = JurorPool::::get(); - if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { + if let Ok(i) = + jurors.binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) + { // remove from juror list to prevent being drawn jurors.remove(i); >::put(jurors); @@ -465,7 +477,7 @@ mod pallet { ensure!( JurorPool::::get() - .binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + .binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) .is_err(), Error::::JurorNotPreparedToExit ); @@ -511,19 +523,19 @@ mod pallet { ); let mut draws = >::get(market_id); - let (index, weight) = match draws.iter().position(|draw| draw.juror == who) { + let (index, weight, slashable) = match draws.iter().position(|draw| draw.juror == who) { Some(index) => { ensure!( matches!(draws[index].vote, Vote::Drawn | Vote::Secret { secret: _ }), Error::::OnlyDrawnJurorsCanVote ); - (index, draws[index].weight) + (index, draws[index].weight, draws[index].slashable) } None => return Err(Error::::OnlyDrawnJurorsCanVote.into()), }; let vote = Vote::Secret { secret: secret_vote }; - draws[index] = Draw { juror: who.clone(), weight, vote }; + draws[index] = Draw { juror: who.clone(), weight, vote, slashable }; >::insert(market_id, draws); @@ -574,8 +586,13 @@ mod pallet { ); let mut draws = >::get(market_id); - let (index, weight, vote) = match draws.iter().position(|draw| draw.juror == juror) { - Some(index) => (index, draws[index].weight, draws[index].vote.clone()), + let (index, weight, vote, slashable) = match draws + .iter() + .position(|draw| draw.juror == juror) + { + Some(index) => { + (index, draws[index].weight, draws[index].vote.clone(), draws[index].slashable) + } None => return Err(Error::::JurorNotDrawn.into()), }; @@ -597,22 +614,22 @@ mod pallet { }; let reward_pot = Self::reward_pot(&market_id); - let free_balance = T::Currency::free_balance(&juror); - let slash = T::DenounceSlashPercentage::get() * T::MinJurorStake::get(); - let slash = slash.min(free_balance); + let slash = T::DenounceSlashPercentage::get() * slashable; let (imbalance, missing) = T::Currency::slash(&juror, slash); debug_assert!(missing.is_zero(), "Could not slash all of the amount."); T::Currency::resolve_creating(&reward_pot, imbalance); let mut jurors = JurorPool::::get(); - if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) { + if let Ok(i) = + jurors.binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) + { // remove from juror list to prevent being drawn jurors.remove(i); >::put(jurors); } let raw_vote = Vote::Denounced { secret, outcome: outcome.clone(), salt }; - draws[index] = Draw { juror: juror.clone(), weight, vote: raw_vote }; + draws[index] = Draw { juror: juror.clone(), weight, vote: raw_vote, slashable }; >::insert(market_id, draws); Self::deposit_event(Event::DenouncedJurorVote { @@ -657,8 +674,13 @@ mod pallet { ); let mut draws = >::get(market_id); - let (index, weight, vote) = match draws.iter().position(|draw| draw.juror == who) { - Some(index) => (index, draws[index].weight, draws[index].vote.clone()), + let (index, weight, vote, slashable) = match draws + .iter() + .position(|draw| draw.juror == who) + { + Some(index) => { + (index, draws[index].weight, draws[index].vote.clone(), draws[index].slashable) + } None => return Err(Error::::JurorNotDrawn.into()), }; @@ -685,7 +707,7 @@ mod pallet { }; let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; - draws[index] = Draw { juror: who.clone(), weight, vote: raw_vote }; + draws[index] = Draw { juror: who.clone(), weight, vote: raw_vote, slashable }; >::insert(market_id, draws); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); @@ -841,28 +863,40 @@ mod pallet { }; let mut jurors = JurorPool::::get(); - let mut slash_and_remove_juror = |ai: &T::AccountId| { - let all_free = T::Currency::free_balance(ai); - let slash = T::TardySlashPercentage::get() * T::MinJurorStake::get(); - let slash = slash.min(all_free); - let (imbalance, missing) = T::Currency::slash(ai, slash); - debug_assert!( - missing.is_zero(), - "Could not slash all of the amount for juror {:?}.", - ai - ); - let reward_pot = Self::reward_pot(&market_id); - T::Currency::resolve_creating(&reward_pot, imbalance); - + let reward_pot = Self::reward_pot(&market_id); + let mut slash_and_remove_juror = |ai: &T::AccountId, slashable: BalanceOf| { if let Some(prev_juror_info) = >::get(ai) { - if let Ok(i) = - jurors.binary_search_by_key(&prev_juror_info.stake, |tuple| tuple.0) + if let Ok(i) = jurors + .binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) { // remove from juror list to prevent being drawn jurors.remove(i); + + let slash = T::TardySlashPercentage::get() * slashable; + let (imbalance, missing) = T::Currency::slash(ai, slash); + debug_assert!( + missing.is_zero(), + "Could not slash all of the amount for juror {:?}.", + ai + ); + + T::Currency::resolve_creating(&reward_pot, imbalance); + } else { + log::warn!( + "Juror {:?} not found in JurorPool storage for vote aggregation. \ + Market id {:?}.", + ai, + market_id + ); + debug_assert!(false); } } else { - log::warn!("Juror {:?} not found in Jurors storage for vote aggregation.", ai); + log::warn!( + "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ + {:?}.", + ai, + market_id + ); debug_assert!(false); } }; @@ -870,12 +904,10 @@ mod pallet { for draw in Draws::::get(market_id).iter() { match draw.vote { Vote::Drawn => { - // TODO slash according to weight? - slash_and_remove_juror(&draw.juror); + slash_and_remove_juror(&draw.juror, draw.slashable); } Vote::Secret { secret: _ } => { - // TODO slash according to weight? - slash_and_remove_juror(&draw.juror); + slash_and_remove_juror(&draw.juror, draw.slashable); } // denounce extrinsic already punished the juror Vote::Denounced { secret: _, outcome: _, salt: _ } => (), @@ -926,8 +958,7 @@ mod pallet { for draw in draws { if let Vote::Revealed { secret: _, outcome, salt: _ } = draw.vote { - // TODO slash and reward according to voting weight! - valid_winners_and_losers.push((draw.juror, outcome)); + valid_winners_and_losers.push((draw.juror, outcome, draw.slashable)); } } @@ -935,7 +966,7 @@ mod pallet { &market_id, valid_winners_and_losers.as_slice(), &winner, - )?; + ); court.status = CourtStatus::Closed { winner, punished: true, reassigned: true }; >::insert(market_id, court); @@ -952,12 +983,16 @@ mod pallet { T: Config, { pub(crate) fn choose_multiple_weighted( - jurors: &[(BalanceOf, T::AccountId)], + jurors: &[JurorPoolItemOf], number: usize, rng: &mut R, ) -> Vec> { - let total_weight = - jurors.iter().map(|(stake, _)| (*stake).saturated_into::()).sum::(); + let total_weight = jurors + .iter() + .map(|pool_item| { + pool_item.stake.saturating_sub(pool_item.slashed).saturated_into::() + }) + .sum::(); let mut random_set = BTreeSet::new(); for _ in 0..number { @@ -965,21 +1000,25 @@ mod pallet { random_set.insert(random_number); } - let mut selections = BTreeMap::::new(); + let mut selections = BTreeMap::)>::new(); let mut current_weight = 0u128; - for (stake, juror) in jurors { + for JurorPoolItem { stake, juror, slashed } in jurors { let lower_bound = current_weight; - let upper_bound = current_weight.saturating_add((*stake).saturated_into::()); + let mut remainder = stake.saturating_sub(*slashed); + let upper_bound = current_weight.saturating_add(remainder.saturated_into::()); // this always gets the lowest random number first and maybe removes it for random_number in random_set.clone().iter() { if &lower_bound <= random_number && random_number < &upper_bound { - if let Some(el) = selections.get_mut(juror) { - *el = el.saturating_add(1); + let slashable = remainder.min(T::MinJurorStake::get()); + if let Some((weight, sel_slashable)) = selections.get_mut(juror) { + *weight = weight.saturating_add(1); + *sel_slashable = sel_slashable.saturating_add(slashable); } else { - selections.insert(juror.clone(), 1); + selections.insert(juror.clone(), (1, slashable)); } + remainder = remainder.saturating_sub(slashable); random_set.remove(random_number); } else { break; @@ -995,13 +1034,18 @@ mod pallet { selections .into_iter() - .map(|(juror, weight)| Draw { juror, weight, vote: Vote::Drawn }) + .map(|(juror, (weight, slashable))| Draw { + juror, + weight, + vote: Vote::Drawn, + slashable, + }) .collect() } pub(crate) fn select_jurors( market_id: &MarketIdOf, - jurors: &[(BalanceOf, T::AccountId)], + jurors: &[JurorPoolItemOf], appeal_number: usize, ) -> Result<(), DispatchError> { let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); @@ -1088,19 +1132,19 @@ mod pallet { fn slash_losers_to_award_winners( market_id: &MarketIdOf, - valid_winners_and_losers: &[(T::AccountId, OutcomeReport)], + valid_winners_and_losers: &[(T::AccountId, OutcomeReport, BalanceOf)], winner_outcome: &OutcomeReport, - ) -> DispatchResult { + ) { let mut total_incentives = >::zero(); + let mut jurors = JurorPool::::get(); + let mut winners = Vec::with_capacity(valid_winners_and_losers.len()); - for (juror, outcome) in valid_winners_and_losers { + for (juror, outcome, slashable) in valid_winners_and_losers { if outcome == winner_outcome { winners.push(juror); } else { - let free_balance = T::Currency::free_balance(&juror); - let slash = T::RedistributionPercentage::get() * T::MinJurorStake::get(); - let slash = slash.min(free_balance); + let slash = T::RedistributionPercentage::get() * *slashable; let (imb, missing) = T::Currency::slash(&juror, slash); debug_assert!( missing.is_zero(), @@ -1108,9 +1152,25 @@ mod pallet { juror ); total_incentives.subsume(imb); + + if let Some(juror_info) = >::get(juror) { + if let Ok(i) = jurors + .binary_search_by_key(&juror_info.stake, |pool_item| pool_item.stake) + { + // remove from juror list to prevent being drawn + jurors[i].slashed = jurors[i].slashed.saturating_add(slash); + } else { + log::warn!("Juror {:?} not found in the pool.", juror); + debug_assert!(false); + } + } else { + log::warn!("Juror {:?} not found in Jurors storage.", juror); + debug_assert!(false); + } } } + // reward from denounce slashes and tardy jurors of this market / court let reward_pot = Self::reward_pot(market_id); let reward = T::Currency::free_balance(&reward_pot); let (imb, missing) = T::Currency::slash(&reward_pot, reward); @@ -1131,7 +1191,7 @@ mod pallet { T::Currency::resolve_creating(&treasury_acc, total_incentives); } - Ok(()) + >::put(jurors); } fn get_winner(draws: &[DrawOf]) -> Result { @@ -1286,7 +1346,7 @@ mod pallet { // TODO maybe add the case that the voting decision is unclear (or NoVotes case) - let jurors: JurorPoolOf = JurorPool::::get(); + let jurors_len: usize = JurorPool::::decode_len().unwrap_or(0); match >::get(market_id) { Some(court) => { let appeals = &court.appeals; @@ -1296,7 +1356,7 @@ mod pallet { Self::check_appealable_market(market_id, &court, now).is_ok(); if valid_period { - if jurors.len() < necessary_jurors_number { + if jurors_len < necessary_jurors_number { has_failed = true; } @@ -1314,7 +1374,7 @@ mod pallet { report_block <= now && now < block_after_dispute_duration; let necessary_jurors_number = Self::necessary_jurors_num(0usize); - if jurors.len() < necessary_jurors_number && during_dispute_duration { + if jurors_len < necessary_jurors_number && during_dispute_duration { has_failed = true; } } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 1b38c134d..927cfdb67 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -34,7 +34,7 @@ use zeitgeist_primitives::{ BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, CourtBackingPeriod, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDrawings, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, - SlashPercentage, BASE, + TardySlashPercentage, BASE, }, traits::DisputeResolutionApi, types::{ @@ -130,7 +130,7 @@ impl crate::Config for Runtime { type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; - type SlashPercentage = SlashPercentage; + type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 3933a4edd..798f35e32 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -152,8 +152,25 @@ impl PartialEq, Eq, )] -pub struct Draw { +pub struct Draw { pub(crate) juror: AccountId, pub(crate) weight: u32, pub(crate) vote: Vote, + pub(crate) slashable: Balance, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct JurorPoolItem { + pub(crate) stake: Balance, + pub(crate) juror: AccountId, + pub(crate) slashed: Balance, } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 36d4e8a55..6d1dfbdee 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -47,7 +47,7 @@ use zeitgeist_primitives::{ MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, RedistributionPercentage, SimpleDisputesPalletId, - SlashPercentage, SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, + SwapsPalletId, TardySlashPercentage, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -293,7 +293,7 @@ impl zrml_court::Config for Runtime { type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; - type SlashPercentage = SlashPercentage; + type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } From 0a50f410670813bccb6e4eeedd74a28239dbbc46 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Feb 2023 14:40:40 +0100 Subject: [PATCH 104/368] detect draw --- zrml/court/src/lib.rs | 129 +++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7e53b9702..66aaae66c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -354,6 +354,9 @@ mod pallet { InsufficientAmount, /// After the first join of the court the amount has to be higher than the last join. AmountBelowLastJoin, + /// If no last winner can be determined the market resolves + /// on the oracle report and is not able to get appealed. + NoLastWinner, } #[pallet::call] @@ -380,11 +383,13 @@ mod pallet { let mut jurors = JurorPool::::get(); + let mut slashed = >::zero(); if let Some(prev_juror_info) = >::get(&who) { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); if let Ok(i) = jurors.binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) { + slashed = jurors[i].slashed; jurors.remove(i); } else { // this happens if the juror behaved incorrectly @@ -400,14 +405,7 @@ mod pallet { // if there are multiple jurors with the same stake Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), Err(i) => jurors - .try_insert( - i, - JurorPoolItem { - stake: amount, - juror: who.clone(), - slashed: >::zero(), - }, - ) + .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), slashed }) .map_err(|_| Error::::MaxJurorsReached)?, }; @@ -523,19 +521,19 @@ mod pallet { ); let mut draws = >::get(market_id); - let (index, weight, slashable) = match draws.iter().position(|draw| draw.juror == who) { + let (index, draw) = match draws.iter().position(|draw| draw.juror == who) { Some(index) => { ensure!( matches!(draws[index].vote, Vote::Drawn | Vote::Secret { secret: _ }), Error::::OnlyDrawnJurorsCanVote ); - (index, draws[index].weight, draws[index].slashable) + (index, draws[index].clone()) } None => return Err(Error::::OnlyDrawnJurorsCanVote.into()), }; let vote = Vote::Secret { secret: secret_vote }; - draws[index] = Draw { juror: who.clone(), weight, vote, slashable }; + draws[index] = Draw { juror: who.clone(), vote, ..draw }; >::insert(market_id, draws); @@ -586,17 +584,12 @@ mod pallet { ); let mut draws = >::get(market_id); - let (index, weight, vote, slashable) = match draws - .iter() - .position(|draw| draw.juror == juror) - { - Some(index) => { - (index, draws[index].weight, draws[index].vote.clone(), draws[index].slashable) - } + let (index, draw) = match draws.iter().position(|draw| draw.juror == juror) { + Some(index) => (index, draws[index].clone()), None => return Err(Error::::JurorNotDrawn.into()), }; - let secret = match vote { + let secret = match draw.vote { Vote::Secret { secret } => { ensure!( secret == T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)), @@ -614,7 +607,7 @@ mod pallet { }; let reward_pot = Self::reward_pot(&market_id); - let slash = T::DenounceSlashPercentage::get() * slashable; + let slash = T::DenounceSlashPercentage::get() * draw.slashable; let (imbalance, missing) = T::Currency::slash(&juror, slash); debug_assert!(missing.is_zero(), "Could not slash all of the amount."); T::Currency::resolve_creating(&reward_pot, imbalance); @@ -629,7 +622,7 @@ mod pallet { } let raw_vote = Vote::Denounced { secret, outcome: outcome.clone(), salt }; - draws[index] = Draw { juror: juror.clone(), weight, vote: raw_vote, slashable }; + draws[index] = Draw { juror: juror.clone(), vote: raw_vote, ..draw }; >::insert(market_id, draws); Self::deposit_event(Event::DenouncedJurorVote { @@ -674,17 +667,12 @@ mod pallet { ); let mut draws = >::get(market_id); - let (index, weight, vote, slashable) = match draws - .iter() - .position(|draw| draw.juror == who) - { - Some(index) => { - (index, draws[index].weight, draws[index].vote.clone(), draws[index].slashable) - } + let (index, draw) = match draws.iter().position(|draw| draw.juror == who) { + Some(index) => (index, draws[index].clone()), None => return Err(Error::::JurorNotDrawn.into()), }; - let secret = match vote { + let secret = match draw.vote { Vote::Secret { secret } => { // market id and current appeal number is part of salt generation // salt should be signed by the juror (market_id ++ appeal number) @@ -707,7 +695,7 @@ mod pallet { }; let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; - draws[index] = Draw { juror: who.clone(), weight, vote: raw_vote, slashable }; + draws[index] = Draw { juror: who.clone(), vote: raw_vote, ..draw }; >::insert(market_id, draws); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); @@ -735,10 +723,15 @@ mod pallet { Self::check_appealable_market(&market_id, &court, now)?; let draws = Draws::::get(market_id); - // TODO: shouldn't fail here, especially when there are no votes an appeal should be necessary or start global dispute - let appealed_outcome = Self::get_winner(draws.as_slice())?; + let appealed_outcome = + Self::get_winner(draws.as_slice()).ok_or(Error::::NoLastWinner)?; + let jurors_len = >::decode_len().unwrap_or(0); let appeal_number = court.appeals.len(); + let necessary_jurors_number = + Self::necessary_jurors_num(appeal_number.saturating_add(1)); + ensure!(jurors_len >= necessary_jurors_number, Error::::NotEnoughJurors); + let bond = default_appeal_bond::(appeal_number); let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; @@ -1012,9 +1005,9 @@ mod pallet { for random_number in random_set.clone().iter() { if &lower_bound <= random_number && random_number < &upper_bound { let slashable = remainder.min(T::MinJurorStake::get()); - if let Some((weight, sel_slashable)) = selections.get_mut(juror) { + if let Some((weight, all_slashable)) = selections.get_mut(juror) { *weight = weight.saturating_add(1); - *sel_slashable = sel_slashable.saturating_add(slashable); + *all_slashable = all_slashable.saturating_add(slashable); } else { selections.insert(juror.clone(), (1, slashable)); } @@ -1194,7 +1187,7 @@ mod pallet { >::put(jurors); } - fn get_winner(draws: &[DrawOf]) -> Result { + fn get_winner(draws: &[DrawOf]) -> Option { let mut scores = BTreeMap::::new(); for draw in draws { @@ -1212,17 +1205,35 @@ mod pallet { let mut best_score = if let Some(first) = iter.next() { first } else { - // TODO(#980): Create another voting round or trigger global dispute - return Err(Error::::NoVotes.into()); + return None; + }; + + let mut second_best_score = if let Some(second) = iter.next() { + if second.1 > best_score.1 { + best_score = second; + best_score + } else { + second + } + } else { + return Some(best_score.0.clone()); }; for el in iter { if el.1 > best_score.1 { best_score = el; + second_best_score = best_score; + } else if el.1 > second_best_score.1 { + second_best_score = el; } } - Ok(best_score.0.clone()) + if best_score.1 == second_best_score.1 { + // TODO(#980): Rather should start a new voting round instead of resolving to the oracle report outcome. + return None; + } + + Some(best_score.0.clone()) } } @@ -1279,19 +1290,8 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let draws = Draws::::get(market_id); - let winner_outcome = Self::get_winner(draws.as_slice())?; - - court.status = CourtStatus::Closed { - winner: winner_outcome.clone(), - punished: false, - reassigned: false, - }; - >::insert(market_id, court); - - Ok(Some(winner_outcome)) + Ok(Self::get_winner(draws.as_slice())) } fn maybe_pay( @@ -1305,17 +1305,25 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - for AppealInfo { backer, bond, appealed_outcome } in court.appeals { - if resolved_outcome == &appealed_outcome { + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + + for AppealInfo { backer, bond, appealed_outcome } in &court.appeals { + if resolved_outcome == appealed_outcome { let (imb, _) = - T::Currency::slash_reserved_named(&Self::reserve_id(), &backer, bond); + T::Currency::slash_reserved_named(&Self::reserve_id(), backer, *bond); overall_imbalance.subsume(imb); } else { - T::Currency::unreserve_named(&Self::reserve_id(), &backer, bond); + T::Currency::unreserve_named(&Self::reserve_id(), backer, *bond); } } + court.status = CourtStatus::Closed { + winner: resolved_outcome.clone(), + punished: false, + reassigned: false, + }; + >::insert(market_id, court); + Ok(overall_imbalance) } @@ -1344,13 +1352,18 @@ mod pallet { let mut has_failed = false; let now = >::block_number(); - // TODO maybe add the case that the voting decision is unclear (or NoVotes case) - let jurors_len: usize = JurorPool::::decode_len().unwrap_or(0); match >::get(market_id) { Some(court) => { let appeals = &court.appeals; - let appeal_number = appeals.len(); + let is_appeal_backed = &court.is_appeal_backed; + let appeal_number = if *is_appeal_backed { + // this case is after the extrinsic call to `back_appeal` + appeals.len() + } else { + // this case is before the extrinsic call to `back_appeal` + appeals.len().saturating_add(1) + }; let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); let valid_period = Self::check_appealable_market(market_id, &court, now).is_ok(); From 87515057af5fc559096634c95ab243d70b143e1a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Feb 2023 10:14:01 +0100 Subject: [PATCH 105/368] satisfy clippy --- primitives/src/constants.rs | 2 + primitives/src/constants/mock.rs | 5 ++- runtime/battery-station/src/parameters.rs | 10 ++++- runtime/common/src/lib.rs | 12 ++--- runtime/zeitgeist/src/parameters.rs | 10 ++++- zrml/court/src/lib.rs | 10 +---- zrml/court/src/mock.rs | 19 +++++--- zrml/court/src/tests.rs | 54 +++++++++++++---------- zrml/prediction-markets/src/mock.rs | 32 ++++++++------ 9 files changed, 92 insertions(+), 62 deletions(-) diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs index 1e707fa6f..bb4ac62e7 100644 --- a/primitives/src/constants.rs +++ b/primitives/src/constants.rs @@ -66,6 +66,8 @@ pub const AUTHORIZED_PALLET_ID: PalletId = PalletId(*b"zge/atzd"); // Court /// Pallet identifier, mainly used for named balance reserves. pub const COURT_PALLET_ID: PalletId = PalletId(*b"zge/cout"); +/// Lock identifier, mainly used for the locks on the accounts. +pub const COURT_LOCK_ID: [u8; 8] = *b"zge/colk"; // Global Disputes pub const GLOBAL_DISPUTES_PALLET_ID: PalletId = PalletId(*b"zge/gldp"); diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 2ebfab425..97c4649c1 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -17,15 +17,18 @@ parameter_types! { // Court parameter_types! { + pub const AppealBond: Balance = 5 * BASE; + pub const AppealBondFactor: Balance = 2 * BASE; pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); pub const CourtBackingPeriod: BlockNumber = 34; pub const CourtVotePeriod: BlockNumber = 34; pub const CourtAggregationPeriod: BlockNumber = 34; pub const CourtAppealPeriod: BlockNumber = 34; + pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); pub const MaxAppeals: u32 = 5; - pub const MaxDrawings: u32 = 191; + pub const MaxDraws: u32 = 191; pub const MaxJurors: u32 = 1000; pub const MinJurorStake: Balance = 50 * CENT; pub const RedistributionPercentage: Percent = Percent::from_percent(10); diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index f6a4372b5..f11b22867 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -77,6 +77,12 @@ parameter_types! { pub const TechnicalCommitteeMotionDuration: BlockNumber = 7 * BLOCKS_PER_DAY; // Court + /// (Slashable) Bond that is provided for overriding the last appeal. + /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` + /// was deposited. + pub const AppealBond: Balance = 5 * BASE; + /// `OutcomeBond` is increased by this factor after every new appeal. + pub const AppealBondFactor: Balance = 2 * BASE; /// Duration of a single court case. pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. @@ -91,12 +97,14 @@ parameter_types! { pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which a court case can get appealed. pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The court lock identifier. + pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDrawings: u32 = 191; + pub const MaxDraws: u32 = 191; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 003ab0815..0c5e3533d 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -935,23 +935,23 @@ macro_rules! impl_config_traits { } impl zrml_court::Config for Runtime { - type AppealOrigin = EitherOfDiverse< - EnsureRoot, - pallet_collective::EnsureMember - >; + type AppealBond = AppealBond; + type AppealBondFactor = AppealBondFactor; type CourtBackingPeriod = CourtBackingPeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; + type CourtLockId = CourtLockId; + type CourtPalletId = CourtPalletId; + type Currency = Balances; type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; - type MaxDrawings = MaxDrawings; + type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; - type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; type TardySlashPercentage = TardySlashPercentage; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 27ff1d879..0486d3abf 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -77,6 +77,12 @@ parameter_types! { pub const TechnicalCommitteeMotionDuration: BlockNumber = 7 * BLOCKS_PER_DAY; // Court + /// (Slashable) Bond that is provided for overriding the last appeal. + /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` + /// was deposited. + pub const AppealBond: Balance = 2000 * BASE; + /// `OutcomeBond` is increased by this factor after every new appeal. + pub const AppealBondFactor: Balance = 2 * BASE; /// Duration of a single court case. pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. @@ -91,12 +97,14 @@ parameter_types! { pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which a court case can get appealed. pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + /// The lock identifier for the court votes. + pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDrawings: u32 = 191; + pub const MaxDraws: u32 = 191; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 66aaae66c..915d00e05 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1138,7 +1138,7 @@ mod pallet { winners.push(juror); } else { let slash = T::RedistributionPercentage::get() * *slashable; - let (imb, missing) = T::Currency::slash(&juror, slash); + let (imb, missing) = T::Currency::slash(juror, slash); debug_assert!( missing.is_zero(), "Could not slash all of the amount for juror {:?}.", @@ -1201,13 +1201,7 @@ mod pallet { } let mut iter = scores.iter(); - - let mut best_score = if let Some(first) = iter.next() { - first - } else { - return None; - }; - + let mut best_score = iter.next()?; let mut second_best_score = if let Some(second) = iter.next() { if second.1 > best_score.1 { best_score = second; diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 927cfdb67..82ceb40b3 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -31,10 +31,10 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, CourtBackingPeriod, - CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDrawings, - MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, - TardySlashPercentage, BASE, + AppealBond, AppealBondFactor, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, + CourtBackingPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, + MaxAppeals, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, + RedistributionPercentage, TardySlashPercentage, BASE, }, traits::DisputeResolutionApi, types::{ @@ -114,7 +114,10 @@ impl DisputeResolutionApi for NoopResolution { } impl crate::Config for Runtime { - type AppealOrigin = frame_system::EnsureSigned; + type AppealBond = AppealBond; + type AppealBondFactor = AppealBondFactor; + type CourtLockId = CourtLockId; + type Currency = Balances; type CourtBackingPeriod = CourtBackingPeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; @@ -124,10 +127,10 @@ impl crate::Config for Runtime { type Event = (); type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; - type MaxDrawings = MaxDrawings; + type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; - type PalletId = CourtPalletId; + type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; type TardySlashPercentage = TardySlashPercentage; @@ -201,6 +204,8 @@ impl Default for ExtBuilder { (ALICE, 1_000 * BASE), (BOB, 1_000 * BASE), (CHARLIE, 1_000 * BASE), + (EVE, 1_000 * BASE), + (DAVE, 1_000 * BASE), (Court::treasury_account_id(), 1_000 * BASE), ], } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a7a71051b..1022e92b1 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -22,14 +22,12 @@ use crate::{ Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, }, - Error, JurorInfo, Jurors, MarketOf, -}; -use frame_support::{ - assert_noop, assert_ok, - traits::{Hooks, NamedReservableCurrency}, + Error, JurorInfo, JurorPoolItem, Jurors, MarketOf, }; +use frame_support::{assert_noop, assert_ok, traits::Hooks}; +use pallet_balances::BalanceLock; use zeitgeist_primitives::{ - constants::BASE, + constants::{mock::CourtLockId, BASE}, traits::DisputeApi, types::{ AccountIdTest, Asset, Deadlines, Market, MarketBonds, MarketCreation, @@ -56,31 +54,35 @@ const DEFAULT_MARKET: MarketOf = Market { bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None }, }; -const DEFAULT_SET_OF_JURORS: &[(u128, AccountIdTest)] = &[ - (9, HARRY), - (8, IAN), - (7, ALICE), - (6, BOB), - (5, CHARLIE), - (4, DAVE), - (3, EVE), - (2, FERDIE), - (1, GINA), +const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ + JurorPoolItem { stake: 9, juror: HARRY, slashed: 0 }, + JurorPoolItem { stake: 8, juror: IAN, slashed: 0 }, + JurorPoolItem { stake: 7, juror: ALICE, slashed: 0 }, + JurorPoolItem { stake: 6, juror: BOB, slashed: 0 }, + JurorPoolItem { stake: 5, juror: CHARLIE, slashed: 0 }, + JurorPoolItem { stake: 4, juror: DAVE, slashed: 0 }, + JurorPoolItem { stake: 3, juror: EVE, slashed: 0 }, + JurorPoolItem { stake: 2, juror: FERDIE, slashed: 0 }, + JurorPoolItem { stake: 1, juror: GINA, slashed: 0 }, ]; +fn the_lock(amount: u128) -> BalanceLock { + BalanceLock { id: CourtLockId::get(), amount, reasons: pallet_balances::Reasons::All } +} + #[test] fn exit_court_successfully_removes_a_juror_and_frees_balances() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!(Jurors::::iter().count(), 1); - assert_eq!(Balances::free_balance(ALICE), 998 * BASE); - assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), amount); + assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE); + assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); assert_eq!(Jurors::::iter().count(), 0); assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE); - assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), 0); + assert_eq!(Balances::locks(ALICE), vec![]); }); } @@ -169,14 +171,14 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = Court::choose_multiple_weighted(&0, DEFAULT_SET_OF_JURORS, 2, &mut rng); + let random_jurors = Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng); let mut at_least_one_set_is_different = false; for _ in 0..100 { setup_blocks(1); let another_set_of_random_jurors = - Court::choose_multiple_weighted(&0, DEFAULT_SET_OF_JURORS, 2, &mut rng); + Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng); let mut iter = another_set_of_random_jurors.iter(); if let Some(juror) = iter.next() { @@ -202,9 +204,9 @@ fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = Court::choose_multiple_weighted(&0, DEFAULT_SET_OF_JURORS, 2, &mut rng); - for (juror, _) in random_jurors { - assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.1 == juror)); + let random_jurors = Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng); + for draw in random_jurors { + assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.juror == draw.juror)); } }); } @@ -216,9 +218,13 @@ fn vote_will_not_accept_unknown_accounts() { let amount_alice = 2 * BASE; let amount_bob = 3 * BASE; let amount_charlie = 4 * BASE; + let amount_eve = 5 * BASE; + let amount_dave = 6 * BASE; Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); + Court::join_court(Origin::signed(EVE), amount_eve).unwrap(); + Court::join_court(Origin::signed(DAVE), amount_dave).unwrap(); Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); }); } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 6d1dfbdee..999c5718b 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -37,17 +37,18 @@ use sp_runtime::{ use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; use zeitgeist_primitives::{ constants::mock::{ - AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, CorrectionPeriod, - CourtAggregationPeriod, CourtAppealPeriod, CourtBackingPeriod, CourtPalletId, - CourtVotePeriod, DenounceSlashPercentage, ExistentialDeposit, ExistentialDeposits, ExitFee, - GetNativeCurrencyId, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, - MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDrawings, MaxEditReasonLen, - MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, - MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, - MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, - MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, - OutcomeFactor, OutsiderBond, PmPalletId, RedistributionPercentage, SimpleDisputesPalletId, - SwapsPalletId, TardySlashPercentage, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, + AppealBond, AppealBondFactor, AuthorizedPalletId, BalanceFractionalDecimals, + BlockHashCount, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, + CourtBackingPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, + ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, + LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, + MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, + MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, + MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, + MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, + MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, + OutsiderBond, PmPalletId, RedistributionPercentage, SimpleDisputesPalletId, SwapsPalletId, + TardySlashPercentage, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -277,20 +278,23 @@ impl zrml_authorized::Config for Runtime { } impl zrml_court::Config for Runtime { + type AppealBond = AppealBond; + type AppealBondFactor = AppealBondFactor; type DisputeResolution = prediction_markets::Pallet; - type AppealOrigin = frame_system::EnsureSigned; type CourtBackingPeriod = CourtBackingPeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; + type CourtLockId = CourtLockId; + type Currency = Balances; type DenounceSlashPercentage = DenounceSlashPercentage; type Event = Event; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; - type MaxDrawings = MaxDrawings; + type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; - type PalletId = CourtPalletId; + type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; type TardySlashPercentage = TardySlashPercentage; From 225ff53d5fa7fe1c036f7a565c7d8132d6bc87d9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Feb 2023 11:45:58 +0100 Subject: [PATCH 106/368] rename backing_period to pre_voting_period --- primitives/src/constants/mock.rs | 2 +- runtime/battery-station/src/parameters.rs | 2 +- runtime/common/src/lib.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/court/src/benchmarks.rs | 4 +- zrml/court/src/lib.rs | 93 ++++++++++++++++------- zrml/court/src/mock.rs | 4 +- zrml/court/src/types.rs | 12 +-- zrml/prediction-markets/src/mock.rs | 6 +- 9 files changed, 82 insertions(+), 45 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 97c4649c1..019e638c9 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -21,7 +21,7 @@ parameter_types! { pub const AppealBondFactor: Balance = 2 * BASE; pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); - pub const CourtBackingPeriod: BlockNumber = 34; + pub const CourtPreVotePeriod: BlockNumber = 34; pub const CourtVotePeriod: BlockNumber = 34; pub const CourtAggregationPeriod: BlockNumber = 34; pub const CourtAppealPeriod: BlockNumber = 34; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index f11b22867..5e230cd24 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -90,7 +90,7 @@ parameter_types! { /// The time to wait before jurors can start voting. /// The intention is to use this period as preparation time /// (for example vote outcome addition through crowdfunding) - pub const CourtBackingPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtPreVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which the jurors can cast their secret vote. pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which the jurors should reveal their secret vote. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 0c5e3533d..747aba63f 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -937,7 +937,7 @@ macro_rules! impl_config_traits { impl zrml_court::Config for Runtime { type AppealBond = AppealBond; type AppealBondFactor = AppealBondFactor; - type CourtBackingPeriod = CourtBackingPeriod; + type CourtPreVotePeriod = CourtPreVotePeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 0486d3abf..da539ed10 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -90,7 +90,7 @@ parameter_types! { /// The time to wait before jurors can start voting. /// The intention is to use this period as preparation time /// (for example vote outcome addition through crowdfunding) - pub const CourtBackingPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtPreVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which the jurors can cast their secret vote. pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which the jurors should reveal their secret vote. diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 68422b8ce..6e382f37b 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -23,7 +23,7 @@ #[cfg(test)] use crate::Pallet as Court; -use crate::{BalanceOf, Call, Config, CurrencyOf, Pallet}; +use crate::{BalanceOf, Call, Config, Pallet}; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; @@ -33,7 +33,7 @@ fn deposit(caller: &T::AccountId) where T: Config, { - let _ = CurrencyOf::::deposit_creating(caller, BalanceOf::::max_value()); + let _ = T::Currency::deposit_creating(caller, BalanceOf::::max_value()); } benchmarks! { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 915d00e05..9973f7c9f 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -85,7 +85,7 @@ mod pallet { /// The intention is to use this period as preparation time /// (for example vote outcome addition through crowdfunding) #[pallet::constant] - type CourtBackingPeriod: Get; + type CourtPreVotePeriod: Get; /// The time in which the jurors can cast their secret vote. #[pallet::constant] @@ -176,16 +176,15 @@ mod pallet { // of the same market const SUBSEQUENT_JURORS_FACTOR: usize = 2; - pub(crate) type BalanceOf = - as Currency<::AccountId>>::Balance; - pub(crate) type CurrencyOf = ::Currency; + pub(crate) type AccountIdOf = ::AccountId; + pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; pub(crate) type NegativeImbalanceOf = - as Currency<::AccountId>>::NegativeImbalance; + <::Currency as Currency>>::NegativeImbalance; pub(crate) type MarketIdOf = <::MarketCommons as MarketCommonsPalletApi>::MarketId; pub(crate) type MomentOf = <::MarketCommons as MarketCommonsPalletApi>::Moment; pub(crate) type MarketOf = Market< - ::AccountId, + AccountIdOf, BalanceOf, ::BlockNumber, MomentOf, @@ -195,16 +194,12 @@ mod pallet { <::Lookup as StaticLookup>::Source; pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; pub(crate) type JurorInfoOf = JurorInfo>; - pub(crate) type JurorPoolItemOf = - JurorPoolItem<::AccountId, BalanceOf>; + pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; - pub(crate) type DrawOf = Draw< - ::AccountId, - BalanceOf, - ::Hash, - >; + pub(crate) type DrawOf = + Draw, BalanceOf, ::Hash>; pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; - pub(crate) type AppealOf = AppealInfo<::AccountId, BalanceOf>; + pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; #[pallet::pallet] @@ -354,9 +349,6 @@ mod pallet { InsufficientAmount, /// After the first join of the court the amount has to be higher than the last join. AmountBelowLastJoin, - /// If no last winner can be determined the market resolves - /// on the oracle report and is not able to get appealed. - NoLastWinner, } #[pallet::call] @@ -513,10 +505,12 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( - court.periods.backing_end < now && now <= court.periods.vote_end, + court.periods.pre_vote_end < now && now <= court.periods.vote_end, Error::::NotInVotingPeriod ); @@ -569,6 +563,8 @@ mod pallet { ) -> DispatchResult { let denouncer = ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + let juror = T::Lookup::lookup(juror)?; ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); @@ -579,7 +575,7 @@ mod pallet { let now = >::block_number(); // ensure in vote period ensure!( - court.periods.backing_end < now && now <= court.periods.vote_end, + court.periods.pre_vote_end < now && now <= court.periods.vote_end, Error::::NotInVotingPeriod ); @@ -658,6 +654,8 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -716,15 +714,16 @@ mod pallet { pub fn back_appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { let who = ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; ensure!(!court.is_appeal_backed, Error::::AppealAlreadyBacked); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - let draws = Draws::::get(market_id); - let appealed_outcome = - Self::get_winner(draws.as_slice()).ok_or(Error::::NoLastWinner)?; + // the outcome which would be resolved on is appealed (including oracle report) + let appealed_outcome = Self::get_last_resolved_outcome(&market_id)?; let jurors_len = >::decode_len().unwrap_or(0); let appeal_number = court.appeals.len(); @@ -772,6 +771,8 @@ mod pallet { ) -> DispatchResult { ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; ensure!(!court.is_drawn, Error::::JurorsAlreadyDrawn); ensure!(court.is_appeal_backed, Error::::BackAppealFirst); @@ -805,13 +806,15 @@ mod pallet { pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; ensure!(court.is_appeal_backed && court.is_drawn, Error::::AppealNotReady); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; let periods = Periods { - backing_end: T::CourtBackingPeriod::get(), + pre_vote_end: T::CourtPreVotePeriod::get(), vote_end: T::CourtVotePeriod::get(), aggregation_end: T::CourtAggregationPeriod::get(), appeal_end: T::CourtAppealPeriod::get(), @@ -846,6 +849,8 @@ mod pallet { ) -> DispatchResult { ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { CourtStatus::Closed { winner, punished, reassigned: _ } => { @@ -935,6 +940,8 @@ mod pallet { ) -> DispatchResult { ensure_signed(origin)?; + T::MarketCommons::market(&market_id)?; + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { CourtStatus::Closed { winner, punished, reassigned } => { @@ -1187,7 +1194,13 @@ mod pallet { >::put(jurors); } - fn get_winner(draws: &[DrawOf]) -> Option { + // Returns the winner of the current court round. + // If there is no element inside `draws`, returns `None`. + // If the best two outcomes have the same score, returns the last court round winner. + fn get_winner( + draws: &[DrawOf], + last_winner: Option, + ) -> Option { let mut scores = BTreeMap::::new(); for draw in draws { @@ -1223,12 +1236,29 @@ mod pallet { } if best_score.1 == second_best_score.1 { - // TODO(#980): Rather should start a new voting round instead of resolving to the oracle report outcome. - return None; + return last_winner; } Some(best_score.0.clone()) } + + fn get_last_resolved_outcome( + market_id: &MarketIdOf, + ) -> Result { + let market = T::MarketCommons::market(market_id)?; + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let last_winner: Option = court + .appeals + .last() + .map(|appeal_info| Some(appeal_info.appealed_outcome.clone())) + .unwrap_or(None); + let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; + let oracle_outcome = report.outcome.clone(); + let draws = Draws::::get(market_id); + let resolved_outcome = + Self::get_winner(draws.as_slice(), last_winner).unwrap_or(oracle_outcome); + Ok(resolved_outcome) + } } impl DisputeApi for Pallet @@ -1258,7 +1288,7 @@ mod pallet { let now = >::block_number(); let periods = Periods { - backing_end: T::CourtBackingPeriod::get(), + pre_vote_end: T::CourtPreVotePeriod::get(), vote_end: T::CourtVotePeriod::get(), aggregation_end: T::CourtAggregationPeriod::get(), appeal_end: T::CourtAppealPeriod::get(), @@ -1284,8 +1314,15 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let last_winner: Option = court + .appeals + .last() + .map(|appeal_info| Some(appeal_info.appealed_outcome.clone())) + .unwrap_or(None); let draws = Draws::::get(market_id); - Ok(Self::get_winner(draws.as_slice())) + // if get_winner returns None, `on_resolution` will fall back on the oracle report + Ok(Self::get_winner(draws.as_slice(), last_winner)) } fn maybe_pay( diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 82ceb40b3..dc74a7629 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -32,7 +32,7 @@ use sp_runtime::{ use zeitgeist_primitives::{ constants::mock::{ AppealBond, AppealBondFactor, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, - CourtBackingPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, + CourtLockId, CourtPalletId, CourtPreVotePeriod, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, TardySlashPercentage, BASE, }, @@ -118,7 +118,7 @@ impl crate::Config for Runtime { type AppealBondFactor = AppealBondFactor; type CourtLockId = CourtLockId; type Currency = Balances; - type CourtBackingPeriod = CourtBackingPeriod; + type CourtPreVotePeriod = CourtPreVotePeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 798f35e32..38d36b2d6 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -59,7 +59,7 @@ pub enum Vote { Eq, )] pub struct Periods { - pub(crate) backing_end: BlockNumber, + pub(crate) pre_vote_end: BlockNumber, pub(crate) vote_end: BlockNumber, pub(crate) aggregation_end: BlockNumber, pub(crate) appeal_end: BlockNumber, @@ -118,11 +118,11 @@ impl CourtInfo { pub fn new(now: BlockNumber, periods: Periods) -> Self { - let backing_end = now.saturating_add(periods.backing_end); - let vote_end = backing_end.saturating_add(periods.vote_end); + let pre_vote_end = now.saturating_add(periods.pre_vote_end); + let vote_end = pre_vote_end.saturating_add(periods.vote_end); let aggregation_end = vote_end.saturating_add(periods.aggregation_end); let appeal_end = aggregation_end.saturating_add(periods.appeal_end); - let periods = Periods { backing_end, vote_end, aggregation_end, appeal_end }; + let periods = Periods { pre_vote_end, vote_end, aggregation_end, appeal_end }; let status = CourtStatus::Open; Self { status, @@ -134,8 +134,8 @@ impl } pub fn update_periods(&mut self, periods: Periods, now: BlockNumber) { - self.periods.backing_end = now.saturating_add(periods.backing_end); - self.periods.vote_end = self.periods.backing_end.saturating_add(periods.vote_end); + self.periods.pre_vote_end = now.saturating_add(periods.pre_vote_end); + self.periods.vote_end = self.periods.pre_vote_end.saturating_add(periods.vote_end); self.periods.aggregation_end = self.periods.vote_end.saturating_add(periods.aggregation_end); self.periods.appeal_end = self.periods.aggregation_end.saturating_add(periods.appeal_end); diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 999c5718b..84ce4fc7b 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -38,8 +38,8 @@ use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; use zeitgeist_primitives::{ constants::mock::{ AppealBond, AppealBondFactor, AuthorizedPalletId, BalanceFractionalDecimals, - BlockHashCount, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, - CourtBackingPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, + BlockHashCount, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, + CourtPalletId, CourtPreVotePeriod, CourtVotePeriod, DenounceSlashPercentage, ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, @@ -281,7 +281,7 @@ impl zrml_court::Config for Runtime { type AppealBond = AppealBond; type AppealBondFactor = AppealBondFactor; type DisputeResolution = prediction_markets::Pallet; - type CourtBackingPeriod = CourtBackingPeriod; + type CourtPreVotePeriod = CourtPreVotePeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; From 0f144818f52b894c93463de532a75319e41051d7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Feb 2023 14:24:18 +0100 Subject: [PATCH 107/368] back global dispute, add request period --- primitives/src/constants/mock.rs | 4 +- runtime/battery-station/src/parameters.rs | 18 ++- runtime/common/src/lib.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 18 ++- zrml/court/src/lib.rs | 133 +++++++++++++++++----- zrml/court/src/mock.rs | 8 +- zrml/court/src/tests.rs | 1 + zrml/prediction-markets/src/lib.rs | 1 - zrml/prediction-markets/src/mock.rs | 20 ++-- 9 files changed, 139 insertions(+), 66 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 019e638c9..db1a0f9dd 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -21,13 +21,13 @@ parameter_types! { pub const AppealBondFactor: Balance = 2 * BASE; pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); - pub const CourtPreVotePeriod: BlockNumber = 34; + pub const RequestInterval: BlockNumber = 50; pub const CourtVotePeriod: BlockNumber = 34; pub const CourtAggregationPeriod: BlockNumber = 34; pub const CourtAppealPeriod: BlockNumber = 34; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); - pub const MaxAppeals: u32 = 5; + pub const MaxAppeals: u32 = 6; pub const MaxDraws: u32 = 191; pub const MaxJurors: u32 = 1000; pub const MinJurorStake: Balance = 50 * CENT; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 5e230cd24..ffbd726ac 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -24,6 +24,7 @@ use super::VERSION; use frame_support::{ parameter_types, + traits::LockIdentifier, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND}, DispatchClass, Weight, @@ -40,9 +41,6 @@ use sp_runtime::{ use sp_version::RuntimeVersion; use zeitgeist_primitives::{constants::*, types::*}; -#[cfg(feature = "with-global-disputes")] -use frame_support::traits::LockIdentifier; - pub(crate) const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); pub(crate) const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_PER_SECOND.ref_time() / 2); @@ -87,22 +85,18 @@ parameter_types! { pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. pub const CourtPalletId: PalletId = COURT_PALLET_ID; - /// The time to wait before jurors can start voting. - /// The intention is to use this period as preparation time - /// (for example vote outcome addition through crowdfunding) - pub const CourtPreVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which the jurors can cast their secret vote. - pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtVotePeriod: BlockNumber = 2 * BLOCKS_PER_DAY; /// The time in which the jurors should reveal their secret vote. - pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtAggregationPeriod: BlockNumber = 2 * BLOCKS_PER_DAY; /// The time in which a court case can get appealed. - pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtAppealPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The court lock identifier. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. - pub const MaxAppeals: u32 = 5; + pub const MaxAppeals: u32 = 6; /// The maximum number of randomly selected jurors for a dispute. pub const MaxDraws: u32 = 191; /// The maximum number of jurors that can be registered. @@ -111,6 +105,8 @@ parameter_types! { pub const MinJurorStake: Balance = 1000 * BASE; /// The percentage that is slashed if a juror did not vote for the plurality outcome. pub const RedistributionPercentage: Percent = Percent::from_percent(10); + /// The interval for requesting multiple court votes at once. + pub const RequestInterval: BlockNumber = 7 * BLOCKS_PER_DAY; /// The percentage that is being slashed from the juror's stake. pub const TardySlashPercentage: Percent = Percent::from_percent(20); diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 747aba63f..6e34e9ca7 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -937,7 +937,6 @@ macro_rules! impl_config_traits { impl zrml_court::Config for Runtime { type AppealBond = AppealBond; type AppealBondFactor = AppealBondFactor; - type CourtPreVotePeriod = CourtPreVotePeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; @@ -954,6 +953,7 @@ macro_rules! impl_config_traits { type MinJurorStake = MinJurorStake; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; + type RequestInterval = RequestInterval; type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index da539ed10..a648b6680 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -24,6 +24,7 @@ use super::VERSION; use frame_support::{ parameter_types, + traits::LockIdentifier, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND}, DispatchClass, Weight, @@ -40,9 +41,6 @@ use sp_runtime::{ use sp_version::RuntimeVersion; use zeitgeist_primitives::{constants::*, types::*}; -#[cfg(feature = "with-global-disputes")] -use frame_support::traits::LockIdentifier; - pub(crate) const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); pub(crate) const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_PER_SECOND.ref_time() / 2); @@ -87,22 +85,18 @@ parameter_types! { pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. pub const CourtPalletId: PalletId = COURT_PALLET_ID; - /// The time to wait before jurors can start voting. - /// The intention is to use this period as preparation time - /// (for example vote outcome addition through crowdfunding) - pub const CourtPreVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; /// The time in which the jurors can cast their secret vote. - pub const CourtVotePeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtVotePeriod: BlockNumber = 2 * BLOCKS_PER_DAY; /// The time in which the jurors should reveal their secret vote. - pub const CourtAggregationPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtAggregationPeriod: BlockNumber = 2 * BLOCKS_PER_DAY; /// The time in which a court case can get appealed. - pub const CourtAppealPeriod: BlockNumber = 7 * BLOCKS_PER_DAY; + pub const CourtAppealPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The lock identifier for the court votes. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. - pub const MaxAppeals: u32 = 5; + pub const MaxAppeals: u32 = 6; /// The maximum number of randomly selected jurors for a dispute. pub const MaxDraws: u32 = 191; /// The maximum number of jurors that can be registered. @@ -111,6 +105,8 @@ parameter_types! { pub const MinJurorStake: Balance = 1000 * BASE; /// The percentage that is slashed if a juror did not vote for the plurality outcome. pub const RedistributionPercentage: Percent = Percent::from_percent(10); + /// The interval for requesting multiple court votes at once. + pub const RequestInterval: BlockNumber = 7 * BLOCKS_PER_DAY; /// The percentage that is being slashed from the juror's stake. pub const TardySlashPercentage: Percent = Percent::from_percent(20); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9973f7c9f..86fa1f308 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -51,7 +51,7 @@ mod pallet { use frame_support::{ dispatch::DispatchResult, ensure, log, - pallet_prelude::{OptionQuery, StorageMap, StorageValue, ValueQuery}, + pallet_prelude::{Hooks, OptionQuery, StorageMap, StorageValue, ValueQuery, Weight}, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, NamedReservableCurrency, Randomness, StorageVersion, WithdrawReasons, @@ -81,12 +81,6 @@ mod pallet { #[pallet::constant] type AppealBondFactor: Get>; - /// The time to wait before jurors can start voting. - /// The intention is to use this period as preparation time - /// (for example vote outcome addition through crowdfunding) - #[pallet::constant] - type CourtPreVotePeriod: Get; - /// The time in which the jurors can cast their secret vote. #[pallet::constant] type CourtVotePeriod: Get; @@ -152,6 +146,10 @@ mod pallet { /// Randomness source type Random: Randomness; + /// The interval for requesting multiple court votes at once. + #[pallet::constant] + type RequestInterval: Get; + /// The percentage that is slashed if a juror did not vote for the plurality outcome. #[pallet::constant] type RedistributionPercentage: Get; @@ -229,6 +227,11 @@ mod pallet { pub type Courts = StorageMap<_, Blake2_128Concat, MarketIdOf, CourtOf, OptionQuery>; + /// The block number in the future when jurors should start voting. + /// This is useful for the user experience of the jurors to vote for multiple courts at once. + #[pallet::storage] + pub type RequestBlock = StorageValue<_, T::BlockNumber, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event @@ -268,6 +271,8 @@ mod pallet { JurorStakesReassigned { market_id: MarketIdOf }, /// The tardy jurors have been punished. TardyJurorsPunished { market_id: MarketIdOf }, + /// The global dispute backing was successful. + GlobalDisputeBacked { market_id: MarketIdOf }, } #[pallet::error] @@ -349,6 +354,23 @@ mod pallet { InsufficientAmount, /// After the first join of the court the amount has to be higher than the last join. AmountBelowLastJoin, + /// The maximum number of normal appeals is reached. So only allow to back a global dispute. + OnlyGlobalDisputeAppealAllowed, + /// In order to back a global dispute, it has to be the last appeal (MaxAppeals reached). + NeedsToBeLastAppeal, + } + + #[pallet::hooks] + impl Hooks for Pallet { + fn on_initialize(now: T::BlockNumber) -> Weight { + let mut total_weight: Weight = Weight::zero(); + if now >= >::get() { + let future_request = now.saturating_add(T::RequestInterval::get()); + >::put(future_request); + total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + } + total_weight + } } #[pallet::call] @@ -517,6 +539,7 @@ mod pallet { let mut draws = >::get(market_id); let (index, draw) = match draws.iter().position(|draw| draw.juror == who) { Some(index) => { + // allow to override last vote ensure!( matches!(draws[index].vote, Vote::Drawn | Vote::Secret { secret: _ }), Error::::OnlyDrawnJurorsCanVote @@ -718,6 +741,11 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; ensure!(!court.is_appeal_backed, Error::::AppealAlreadyBacked); + let appeal_number = court.appeals.len(); + ensure!( + appeal_number < >::bound().saturating_sub(1), + Error::::OnlyGlobalDisputeAppealAllowed + ); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; @@ -726,7 +754,6 @@ mod pallet { let appealed_outcome = Self::get_last_resolved_outcome(&market_id)?; let jurors_len = >::decode_len().unwrap_or(0); - let appeal_number = court.appeals.len(); let necessary_jurors_number = Self::necessary_jurors_num(appeal_number.saturating_add(1)); ensure!(jurors_len >= necessary_jurors_number, Error::::NotEnoughJurors); @@ -734,7 +761,10 @@ mod pallet { let bond = default_appeal_bond::(appeal_number); let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; - court.appeals.try_push(appeal_info).map_err(|_| Error::::MaxAppealsReached)?; + court.appeals.try_push(appeal_info).map_err(|_| { + debug_assert!(false, "Appeal bound is checked above."); + Error::::MaxAppealsReached + })?; T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; @@ -753,6 +783,57 @@ mod pallet { Ok(()) } + /// Back the global dispute to allow the market to be resolved + /// if the last appeal outcome is disagreed on. + /// + /// # Arguments + /// + /// - `market_id`: The identifier of the court. + /// + /// # Weight + /// + /// Complexity: `O(1)` + #[pallet::weight(1_000_000_000_000)] + #[transactional] + pub fn back_global_dispute( + origin: OriginFor, + market_id: MarketIdOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + T::MarketCommons::market(&market_id)?; + + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let appeal_number = court.appeals.len(); + ensure!( + appeal_number == >::bound().saturating_sub(1), + Error::::NeedsToBeLastAppeal + ); + + let now = >::block_number(); + Self::check_appealable_market(&market_id, &court, now)?; + + // the outcome which would be resolved on is appealed (including oracle report) + let appealed_outcome = Self::get_last_resolved_outcome(&market_id)?; + + let bond = default_appeal_bond::(appeal_number); + let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; + + court.appeals.try_push(appeal_info).map_err(|_| { + debug_assert!(false, "Appeal bound is checked above."); + Error::::MaxAppealsReached + })?; + + T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; + + let last_resolve_at = court.periods.appeal_end; + let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + + Self::deposit_event(Event::GlobalDisputeBacked { market_id }); + + Ok(()) + } + /// Randomly select jurors from the pool according to their stake /// for the coming appeal round. /// @@ -813,8 +894,11 @@ mod pallet { let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; + let request_block = >::get(); + debug_assert!(request_block >= now, "Request block must be greater than now."); + let pre_vote_end = request_block.saturating_sub(now); let periods = Periods { - pre_vote_end: T::CourtPreVotePeriod::get(), + pre_vote_end, vote_end: T::CourtVotePeriod::get(), aggregation_end: T::CourtAggregationPeriod::get(), appeal_end: T::CourtAppealPeriod::get(), @@ -996,7 +1080,7 @@ mod pallet { let mut random_set = BTreeSet::new(); for _ in 0..number { - let random_number = rng.gen_range(0u128..total_weight); + let random_number = rng.gen_range(0u128..=total_weight); random_set.insert(random_number); } @@ -1286,9 +1370,12 @@ mod pallet { Self::select_jurors(market_id, jurors.as_slice(), appeal_number)?; let now = >::block_number(); + let request_block = >::get(); + debug_assert!(request_block >= now, "Request block must be greater than now."); + let pre_vote_end = request_block.saturating_sub(now); let periods = Periods { - pre_vote_end: T::CourtPreVotePeriod::get(), + pre_vote_end, vote_end: T::CourtVotePeriod::get(), aggregation_end: T::CourtAggregationPeriod::get(), appeal_end: T::CourtAppealPeriod::get(), @@ -1399,14 +1486,9 @@ mod pallet { let valid_period = Self::check_appealable_market(market_id, &court, now).is_ok(); - if valid_period { - if jurors_len < necessary_jurors_number { - has_failed = true; - } - - if appeals.is_full() { - has_failed = true; - } + if appeals.is_full() || (valid_period && (jurors_len < necessary_jurors_number)) + { + has_failed = true; } } None => { @@ -1418,7 +1500,7 @@ mod pallet { report_block <= now && now < block_after_dispute_duration; let necessary_jurors_number = Self::necessary_jurors_num(0usize); - if jurors_len < necessary_jurors_number && during_dispute_duration { + if during_dispute_duration && jurors_len < necessary_jurors_number { has_failed = true; } } @@ -1433,11 +1515,8 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - if let Some(court) = >::get(market_id) { - let last_resolve_at = court.periods.appeal_end; - let _ids_len_0 = - T::DisputeResolution::remove_auto_resolve(market_id, last_resolve_at); - } + >::remove(market_id); + >::remove(market_id); Ok(()) } @@ -1447,8 +1526,10 @@ mod pallet { market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); + >::remove(market_id); >::remove(market_id); + Ok(()) } } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index dc74a7629..59fc518df 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -32,9 +32,9 @@ use sp_runtime::{ use zeitgeist_primitives::{ constants::mock::{ AppealBond, AppealBondFactor, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, - CourtLockId, CourtPalletId, CourtPreVotePeriod, CourtVotePeriod, DenounceSlashPercentage, - MaxAppeals, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, - RedistributionPercentage, TardySlashPercentage, BASE, + CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDraws, + MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, + RequestInterval, TardySlashPercentage, BASE, }, traits::DisputeResolutionApi, types::{ @@ -118,7 +118,6 @@ impl crate::Config for Runtime { type AppealBondFactor = AppealBondFactor; type CourtLockId = CourtLockId; type Currency = Balances; - type CourtPreVotePeriod = CourtPreVotePeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; @@ -133,6 +132,7 @@ impl crate::Config for Runtime { type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; + type RequestInterval = RequestInterval; type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = crate::weights::WeightInfo; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 1022e92b1..5dcb49199 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -237,6 +237,7 @@ fn setup_blocks(num_blocks: u32) { System::initialize(¤t_block_number, &parent_block_hash, ¤t_digest); RandomnessCollectiveFlip::on_initialize(current_block_number); + Court::on_initialize(current_block_number); System::finalize(); System::set_block_number(current_block_number); } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 1db7adc0d..7d65ab777 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1461,7 +1461,6 @@ mod pallet { } }; - // TODO(#372): Allow court with global disputes. // ensure, that global disputes controls the resolution now // it does not end after the dispute period now, but after the global dispute end diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 84ce4fc7b..dce8e740e 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -39,15 +39,15 @@ use zeitgeist_primitives::{ constants::mock::{ AppealBond, AppealBondFactor, AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, - CourtPalletId, CourtPreVotePeriod, CourtVotePeriod, DenounceSlashPercentage, - ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, - LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, - MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, - MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, - MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, - MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, - MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, - OutsiderBond, PmPalletId, RedistributionPercentage, SimpleDisputesPalletId, SwapsPalletId, + CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, ExistentialDeposit, + ExistentialDeposits, ExitFee, GetNativeCurrencyId, LiquidityMiningPalletId, MaxAppeals, + MaxApprovals, MaxAssets, MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDraws, + MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, + MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, + MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, + MinJurorStake, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, + MinimumPeriod, OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, + RedistributionPercentage, RequestInterval, SimpleDisputesPalletId, SwapsPalletId, TardySlashPercentage, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ @@ -281,7 +281,6 @@ impl zrml_court::Config for Runtime { type AppealBond = AppealBond; type AppealBondFactor = AppealBondFactor; type DisputeResolution = prediction_markets::Pallet; - type CourtPreVotePeriod = CourtPreVotePeriod; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; @@ -297,6 +296,7 @@ impl zrml_court::Config for Runtime { type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RedistributionPercentage = RedistributionPercentage; + type RequestInterval = RequestInterval; type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; From b6d6ec60a76cf6b887c4ba475990f7f79fd1db87 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Feb 2023 14:45:50 +0100 Subject: [PATCH 108/368] improve code readability --- zrml/court/src/lib.rs | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 86fa1f308..cdd7feed7 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -751,7 +751,7 @@ mod pallet { Self::check_appealable_market(&market_id, &court, now)?; // the outcome which would be resolved on is appealed (including oracle report) - let appealed_outcome = Self::get_last_resolved_outcome(&market_id)?; + let appealed_outcome = Self::get_latest_resolved_outcome(&market_id)?; let jurors_len = >::decode_len().unwrap_or(0); let necessary_jurors_number = @@ -814,7 +814,7 @@ mod pallet { Self::check_appealable_market(&market_id, &court, now)?; // the outcome which would be resolved on is appealed (including oracle report) - let appealed_outcome = Self::get_last_resolved_outcome(&market_id)?; + let appealed_outcome = Self::get_latest_resolved_outcome(&market_id)?; let bond = default_appeal_bond::(appeal_number); let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; @@ -1326,7 +1326,7 @@ mod pallet { Some(best_score.0.clone()) } - fn get_last_resolved_outcome( + fn get_latest_resolved_outcome( market_id: &MarketIdOf, ) -> Result { let market = T::MarketCommons::market(market_id)?; @@ -1401,15 +1401,17 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let last_winner: Option = court - .appeals - .last() - .map(|appeal_info| Some(appeal_info.appealed_outcome.clone())) - .unwrap_or(None); - let draws = Draws::::get(market_id); + let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; // if get_winner returns None, `on_resolution` will fall back on the oracle report - Ok(Self::get_winner(draws.as_slice(), last_winner)) + let resolved_outcome = Self::get_latest_resolved_outcome(market_id)?; + court.status = CourtStatus::Closed { + winner: resolved_outcome.clone(), + punished: false, + reassigned: false, + }; + >::insert(market_id, court); + + Ok(Some(resolved_outcome)) } fn maybe_pay( @@ -1423,8 +1425,7 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; for AppealInfo { backer, bond, appealed_outcome } in &court.appeals { if resolved_outcome == appealed_outcome { let (imb, _) = @@ -1435,13 +1436,6 @@ mod pallet { } } - court.status = CourtStatus::Closed { - winner: resolved_outcome.clone(), - punished: false, - reassigned: false, - }; - >::insert(market_id, court); - Ok(overall_imbalance) } From 637b9dc38f8a5047938f38ba6956a0e950c44656 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 1 Mar 2023 08:53:12 +0100 Subject: [PATCH 109/368] combine to one appeal --- zrml/court/src/lib.rs | 189 ++++++++++------------------------------ zrml/court/src/types.rs | 10 +-- 2 files changed, 49 insertions(+), 150 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index cdd7feed7..80a81f408 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -54,7 +54,8 @@ mod pallet { pallet_prelude::{Hooks, OptionQuery, StorageMap, StorageValue, ValueQuery, Weight}, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, - NamedReservableCurrency, Randomness, StorageVersion, WithdrawReasons, + NamedReservableCurrency, Randomness, ReservableCurrency, StorageVersion, + WithdrawReasons, }, transactional, Blake2_128Concat, BoundedVec, PalletId, }; @@ -261,10 +262,6 @@ mod pallet { outcome: OutcomeReport, salt: T::Hash, }, - /// The jurors for an appeal have been drawn. - AppealJurorsDrawn { market_id: MarketIdOf }, - /// The backing for an appeal has been checked. - AppealBacked { market_id: MarketIdOf }, /// A market has been appealed. MarketAppealed { market_id: MarketIdOf, appeal_number: u32 }, /// The juror stakes have been reassigned. @@ -303,15 +300,8 @@ mod pallet { NotInAppealPeriod, /// The court is already present for this market. CourtAlreadyExists, - /// For this appeal round the random juror selection extrinsic was already called. - JurorsAlreadyDrawn, /// For this appeal round the backing check extrinsic was already called. AppealAlreadyBacked, - /// In order to start an appeal the backing check extrinsic must be called first. - BackAppealFirst, - /// The final appeal extrinsic can only be called after the backing check extrinsic - /// and random selection of jurors for this appeal. - AppealNotReady, /// The caller of this extrinsic must be a randomly selected juror. OnlyDrawnJurorsCanVote, /// The amount is below the minimum required stake. @@ -397,6 +387,7 @@ mod pallet { let mut jurors = JurorPool::::get(); + // TODO: maybe remove the lowest bonded juror let mut slashed = >::zero(); if let Some(prev_juror_info) = >::get(&who) { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); @@ -494,6 +485,8 @@ mod pallet { Error::::JurorNotPreparedToExit ); + // TODO this belongs to prepare_exit_court and this extrinsic should be called whenever a juror is removed from the pool (use state which recognizes that the call to this extrinsic is allowed) + // TODO unbounded iteration is dangerous here // ensure not drawn for any market for (_, draws) in >::iter() { ensure!(!draws.iter().any(|draw| draw.juror == juror), Error::::JurorStillDrawn); @@ -527,8 +520,6 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - T::MarketCommons::market(&market_id)?; - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( @@ -586,8 +577,6 @@ mod pallet { ) -> DispatchResult { let denouncer = ensure_signed(origin)?; - T::MarketCommons::market(&market_id)?; - let juror = T::Lookup::lookup(juror)?; ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); @@ -677,8 +666,6 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - T::MarketCommons::market(&market_id)?; - ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -723,7 +710,7 @@ mod pallet { Ok(()) } - /// Back an appeal of a court to get an appeal initiated. + /// Trigger an appeal for a court. /// /// # Arguments /// @@ -731,54 +718,59 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(1)` + /// Complexity: `O(n)`, where `n` is the number of jurors. + /// It depends heavily on `choose_multiple_weighted` of `select_jurors`. #[pallet::weight(1_000_000_000_000)] #[transactional] - pub fn back_appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { let who = ensure_signed(origin)?; - T::MarketCommons::market(&market_id)?; - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - ensure!(!court.is_appeal_backed, Error::::AppealAlreadyBacked); - let appeal_number = court.appeals.len(); + let appeal_number = court.appeals.len().saturating_add(1); + let bond = default_appeal_bond::(appeal_number); + ensure!(T::Currency::can_reserve(&who, bond), Error::::InsufficientAmount); ensure!( - appeal_number < >::bound().saturating_sub(1), + appeal_number < >::bound(), Error::::OnlyGlobalDisputeAppealAllowed ); - let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - // the outcome which would be resolved on is appealed (including oracle report) let appealed_outcome = Self::get_latest_resolved_outcome(&market_id)?; - - let jurors_len = >::decode_len().unwrap_or(0); - let necessary_jurors_number = - Self::necessary_jurors_num(appeal_number.saturating_add(1)); - ensure!(jurors_len >= necessary_jurors_number, Error::::NotEnoughJurors); - - let bond = default_appeal_bond::(appeal_number); let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; - court.appeals.try_push(appeal_info).map_err(|_| { debug_assert!(false, "Appeal bound is checked above."); Error::::MaxAppealsReached })?; - T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; + let jurors: JurorPoolOf = JurorPool::::get(); + Self::select_jurors(&market_id, jurors.as_slice(), appeal_number)?; let last_resolve_at = court.periods.appeal_end; + let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); - court.is_appeal_backed = true; - >::insert(market_id, court); + let request_block = >::get(); + debug_assert!(request_block >= now, "Request block must be greater than now."); + let pre_vote_end = request_block.saturating_sub(now); + let periods = Periods { + pre_vote_end, + vote_end: T::CourtVotePeriod::get(), + aggregation_end: T::CourtAggregationPeriod::get(), + appeal_end: T::CourtAppealPeriod::get(), + }; + // sets periods one after the other from now + court.update_periods(periods, now); + let new_resolve_at = court.periods.appeal_end; + debug_assert!(new_resolve_at != last_resolve_at); + let _ids_len_1 = + T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; - // we want to avoid the resolution before the full appeal is executed - // So, the appeal is inevitable after the call to this extrinsic - // otherwise the market is not going to resolve - let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; - Self::deposit_event(Event::AppealBacked { market_id }); + >::insert(market_id, court); + + let appeal_number = appeal_number as u32; + Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); Ok(()) } @@ -801,8 +793,6 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - T::MarketCommons::market(&market_id)?; - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let appeal_number = court.appeals.len(); ensure!( @@ -834,88 +824,6 @@ mod pallet { Ok(()) } - /// Randomly select jurors from the pool according to their stake - /// for the coming appeal round. - /// - /// # Arguments - /// - /// - `market_id`: The identifier of the court. - /// - /// # Weight - /// - /// Complexity: `O(n)`, where `n` depends on `choose_multiple_weighted` of `select_jurors`. - #[pallet::weight(1_000_000_000_000)] - #[transactional] - pub fn draw_appeal_jurors( - origin: OriginFor, - market_id: MarketIdOf, - ) -> DispatchResult { - ensure_signed(origin)?; - - T::MarketCommons::market(&market_id)?; - - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - ensure!(!court.is_drawn, Error::::JurorsAlreadyDrawn); - ensure!(court.is_appeal_backed, Error::::BackAppealFirst); - let now = >::block_number(); - Self::check_appealable_market(&market_id, &court, now)?; - - let jurors: JurorPoolOf = JurorPool::::get(); - let appeal_number = court.appeals.len(); - Self::select_jurors(&market_id, jurors.as_slice(), appeal_number)?; - - court.is_drawn = true; - >::insert(market_id, court); - - Self::deposit_event(Event::AppealJurorsDrawn { market_id }); - - Ok(()) - } - - /// Trigger an appeal for a court. - /// - /// # Arguments - /// - /// - `market_id`: The identifier of the court. - /// - /// # Weight - /// - /// Complexity: `O(n)`, where `n` is the number of market ids - /// inside the dispute resolution list. - #[pallet::weight(1_000_000_000_000)] - #[transactional] - pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { - ensure_signed(origin)?; - - T::MarketCommons::market(&market_id)?; - - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - ensure!(court.is_appeal_backed && court.is_drawn, Error::::AppealNotReady); - let now = >::block_number(); - Self::check_appealable_market(&market_id, &court, now)?; - - let request_block = >::get(); - debug_assert!(request_block >= now, "Request block must be greater than now."); - let pre_vote_end = request_block.saturating_sub(now); - let periods = Periods { - pre_vote_end, - vote_end: T::CourtVotePeriod::get(), - aggregation_end: T::CourtAggregationPeriod::get(), - appeal_end: T::CourtAppealPeriod::get(), - }; - // sets periods one after the other from now - court.update_periods(periods, now); - let appeal_number = court.appeals.len() as u32; - let _ids_len_1 = - T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; - - >::insert(market_id, court); - - Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); - - Ok(()) - } - /// After the court is closed (resolution happened), the tardy jurors can get punished. /// /// # Arguments @@ -933,8 +841,6 @@ mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - T::MarketCommons::market(&market_id)?; - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { CourtStatus::Closed { winner, punished, reassigned: _ } => { @@ -1024,8 +930,6 @@ mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - T::MarketCommons::market(&market_id)?; - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { CourtStatus::Closed { winner, punished, reassigned } => { @@ -1144,6 +1048,17 @@ mod pallet { // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 // MaxDraws should be 191 in this case let draws = >::truncate_from(random_jurors); + debug_assert!( + if appeal_number > 0 { + appeal_number == >::get(market_id).unwrap().appeals.len() + } else { + // for the first court round, we have no previous winner + true + }, + "Before the draws are overwritten, we need to ensure that the winner of the last \ + appeal round was determined with 'get_latest_resolved_outcome' (the data of the \ + last Draws)." + ); // new appeal round should have a fresh set of draws >::insert(market_id, draws); @@ -1402,7 +1317,6 @@ mod pallet { ); let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - // if get_winner returns None, `on_resolution` will fall back on the oracle report let resolved_outcome = Self::get_latest_resolved_outcome(market_id)?; court.status = CourtStatus::Closed { winner: resolved_outcome.clone(), @@ -1468,14 +1382,7 @@ mod pallet { match >::get(market_id) { Some(court) => { let appeals = &court.appeals; - let is_appeal_backed = &court.is_appeal_backed; - let appeal_number = if *is_appeal_backed { - // this case is after the extrinsic call to `back_appeal` - appeals.len() - } else { - // this case is before the extrinsic call to `back_appeal` - appeals.len().saturating_add(1) - }; + let appeal_number = appeals.len().saturating_add(1); let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); let valid_period = Self::check_appealable_market(market_id, &court, now).is_ok(); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 38d36b2d6..a6ff47312 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -108,8 +108,6 @@ pub struct AppealInfo { )] pub struct CourtInfo { pub(crate) status: CourtStatus, - pub(crate) is_appeal_backed: bool, - pub(crate) is_drawn: bool, pub(crate) appeals: Appeals, pub(crate) periods: Periods, } @@ -124,13 +122,7 @@ impl let appeal_end = aggregation_end.saturating_add(periods.appeal_end); let periods = Periods { pre_vote_end, vote_end, aggregation_end, appeal_end }; let status = CourtStatus::Open; - Self { - status, - is_appeal_backed: false, - is_drawn: false, - appeals: Default::default(), - periods, - } + Self { status, appeals: Default::default(), periods } } pub fn update_periods(&mut self, periods: Periods, now: BlockNumber) { From 18caca814d42c7db3925b81185fc08508f8a424f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 1 Mar 2023 11:00:10 +0100 Subject: [PATCH 110/368] replace weakest juror, catch bugs --- zrml/court/src/lib.rs | 128 +++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 80a81f408..dde95974c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -313,8 +313,8 @@ mod pallet { /// In order to exit the court the juror has to exit /// the pool first with `prepare_exit_court`. JurorNotPreparedToExit, - /// The juror was not found in the pool. This happens after `prepare_exit_court` - /// or if the juror did not vote for plurality decisions. + /// The juror was not found in the pool. + JurorAlreadyPreparedToExit, /// The juror needs to exit the court and then rejoin. JurorNeedsToExit, /// The juror was not randomly selected for the court. @@ -369,6 +369,7 @@ mod pallet { /// for court cases according to the provided stake. /// The probability to get selected is higher the more funds are staked. /// The amount is added to the stake-weighted pool. + /// If the pool is full, the juror with the lowest stake is removed from the juror pool. /// /// # Arguments /// @@ -387,21 +388,28 @@ mod pallet { let mut jurors = JurorPool::::get(); - // TODO: maybe remove the lowest bonded juror + if jurors.is_full() { + // remove the lowest staked juror + debug_assert!( + jurors + .first() + .map(|pool_item| pool_item.stake) + .unwrap_or(>::zero()) + <= jurors + .last() + .map(|pool_item| pool_item.stake) + .unwrap_or(>::zero()) + ); + jurors.remove(0); + } + let mut slashed = >::zero(); if let Some(prev_juror_info) = >::get(&who) { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); - if let Ok(i) = - jurors.binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) - { - slashed = jurors[i].slashed; - jurors.remove(i); - } else { - // this happens if the juror behaved incorrectly - // (was denounced, did not reveal, did not vote) - // or if `prepare_exit_court` was called - return Err(Error::::JurorNeedsToExit.into()); - } + let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake) + .ok_or(Error::::JurorNeedsToExit)?; + slashed = pool_item.slashed; + jurors.remove(index); } match jurors.binary_search_by_key(&amount, |pool_item| pool_item.stake) { @@ -427,6 +435,7 @@ mod pallet { /// Prepare as a juror to exit the court. /// For this the juror has to be removed from the stake weighted pool first before the exit. + /// Returns an error if the juror is already not part of the pool anymore. /// /// # Weight /// @@ -440,15 +449,14 @@ mod pallet { let mut jurors = JurorPool::::get(); - if let Ok(i) = - jurors.binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) - { - // remove from juror list to prevent being drawn - jurors.remove(i); + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + jurors.remove(index); >::put(jurors); } else { - // this happens if the juror was slashed by the vote aggregation - return Err(Error::::JurorNeedsToExit.into()); + // this error can happen if the lowest bonded juror was removed + // it can also happen when juror was denounced, + // did not vote or reveal + return Err(Error::::JurorAlreadyPreparedToExit.into()); } Self::deposit_event(Event::JurorPreparedExit { juror: who }); @@ -478,10 +486,9 @@ mod pallet { let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + let jurors = JurorPool::::get(); ensure!( - JurorPool::::get() - .binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) - .is_err(), + Self::get_pool_item(&jurors, prev_juror_info.stake).is_none(), Error::::JurorNotPreparedToExit ); @@ -621,11 +628,8 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); let mut jurors = JurorPool::::get(); - if let Ok(i) = - jurors.binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) - { - // remove from juror list to prevent being drawn - jurors.remove(i); + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + jurors.remove(index); >::put(jurors); } @@ -794,11 +798,8 @@ mod pallet { let who = ensure_signed(origin)?; let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let appeal_number = court.appeals.len(); - ensure!( - appeal_number == >::bound().saturating_sub(1), - Error::::NeedsToBeLastAppeal - ); + let appeal_number = court.appeals.len().saturating_add(1); + ensure!(appeal_number == >::bound(), Error::::NeedsToBeLastAppeal); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; @@ -853,30 +854,18 @@ mod pallet { let mut jurors = JurorPool::::get(); let reward_pot = Self::reward_pot(&market_id); let mut slash_and_remove_juror = |ai: &T::AccountId, slashable: BalanceOf| { + let slash = T::TardySlashPercentage::get() * slashable; + let (imbalance, missing) = T::Currency::slash(ai, slash); + debug_assert!( + missing.is_zero(), + "Could not slash all of the amount for juror {:?}.", + ai + ); + T::Currency::resolve_creating(&reward_pot, imbalance); + if let Some(prev_juror_info) = >::get(ai) { - if let Ok(i) = jurors - .binary_search_by_key(&prev_juror_info.stake, |pool_item| pool_item.stake) - { - // remove from juror list to prevent being drawn - jurors.remove(i); - - let slash = T::TardySlashPercentage::get() * slashable; - let (imbalance, missing) = T::Currency::slash(ai, slash); - debug_assert!( - missing.is_zero(), - "Could not slash all of the amount for juror {:?}.", - ai - ); - - T::Currency::resolve_creating(&reward_pot, imbalance); - } else { - log::warn!( - "Juror {:?} not found in JurorPool storage for vote aggregation. \ - Market id {:?}.", - ai, - market_id - ); - debug_assert!(false); + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + jurors.remove(index); } } else { log::warn!( @@ -1065,6 +1054,21 @@ mod pallet { Ok(()) } + // Returns (index, pool_item) if the stake associated with the juror was found. + // It returns None otherwise. + pub(crate) fn get_pool_item( + jurors: &[JurorPoolItemOf], + stake: BalanceOf, + ) -> Option<(usize, &JurorPoolItemOf)> { + if let Ok(i) = jurors.binary_search_by_key(&stake, |pool_item| pool_item.stake) { + return Some((i, &jurors[i])); + } + // this None case can happen when the lowest bonded juror was removed (`join_court`) + // it can also happen when juror was denounced, + // did not vote or reveal or when the juror decided to leave the court + return None; + } + pub(crate) fn check_appealable_market( market_id: &MarketIdOf, court: &CourtOf, @@ -1153,14 +1157,8 @@ mod pallet { total_incentives.subsume(imb); if let Some(juror_info) = >::get(juror) { - if let Ok(i) = jurors - .binary_search_by_key(&juror_info.stake, |pool_item| pool_item.stake) - { - // remove from juror list to prevent being drawn - jurors[i].slashed = jurors[i].slashed.saturating_add(slash); - } else { - log::warn!("Juror {:?} not found in the pool.", juror); - debug_assert!(false); + if let Some((index, _)) = Self::get_pool_item(&jurors, juror_info.stake) { + jurors[index].slashed = jurors[index].slashed.saturating_add(slash); } } else { log::warn!("Juror {:?} not found in Jurors storage.", juror); From 108bd45498a15769348966f2695c5fad9a202b11 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 1 Mar 2023 11:35:46 +0100 Subject: [PATCH 111/368] fix clippy --- zrml/court/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index dde95974c..5044deca2 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -394,11 +394,11 @@ mod pallet { jurors .first() .map(|pool_item| pool_item.stake) - .unwrap_or(>::zero()) + .unwrap_or_else(>::zero) <= jurors .last() .map(|pool_item| pool_item.stake) - .unwrap_or(>::zero()) + .unwrap_or_else(>::zero) ); jurors.remove(0); } @@ -1066,7 +1066,7 @@ mod pallet { // this None case can happen when the lowest bonded juror was removed (`join_court`) // it can also happen when juror was denounced, // did not vote or reveal or when the juror decided to leave the court - return None; + None } pub(crate) fn check_appealable_market( From e8f1d516983cd5472f54e822dca667487dde070e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 1 Mar 2023 14:43:46 +0100 Subject: [PATCH 112/368] add iteration limit, prevent bugs --- primitives/src/constants/mock.rs | 1 + runtime/battery-station/src/parameters.rs | 2 + runtime/common/src/lib.rs | 1 + runtime/zeitgeist/src/parameters.rs | 2 + zrml/court/src/lib.rs | 83 ++++++++++++++++------- zrml/court/src/mock.rs | 7 +- zrml/court/src/types.rs | 14 ++++ zrml/prediction-markets/src/mock.rs | 7 +- 8 files changed, 87 insertions(+), 30 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index db1a0f9dd..568429308 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -27,6 +27,7 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = 34; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); + pub const IterationLimit: u32 = 100; pub const MaxAppeals: u32 = 6; pub const MaxDraws: u32 = 191; pub const MaxJurors: u32 = 1000; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index ffbd726ac..4b9042ef6 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -95,6 +95,8 @@ parameter_types! { pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); + /// The iteration limit over all markets for the randomly selected jurors. + pub const IterationLimit: u32 = 100; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 6; /// The maximum number of randomly selected jurors for a dispute. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 6e34e9ca7..382efbc7f 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -946,6 +946,7 @@ macro_rules! impl_config_traits { type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; + type IterationLimit = IterationLimit; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index a648b6680..132a00021 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -95,6 +95,8 @@ parameter_types! { pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); + /// The iteration limit over all markets for the randomly selected jurors. + pub const IterationLimit: u32 = 100; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 6; /// The maximum number of randomly selected jurors for a dispute. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 5044deca2..d9b0d3779 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -41,7 +41,7 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, - JurorInfo, JurorPoolItem, Periods, Vote, + ExitRequest, JurorInfo, JurorPoolItem, Periods, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -122,6 +122,9 @@ mod pallet { /// Event type Event: From> + IsType<::Event>; + #[pallet::constant] + type IterationLimit: Get; + /// Market commons type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, @@ -200,6 +203,7 @@ mod pallet { pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; + pub(crate) type ExitRequestOf = ExitRequest>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -214,6 +218,11 @@ mod pallet { pub type Jurors = StorageMap<_, Blake2_128Concat, T::AccountId, JurorInfoOf, OptionQuery>; + /// If a juror wants to exit the court, this information is required for a multiblock execution. + #[pallet::storage] + pub type ExitRequests = + StorageMap<_, Blake2_128Concat, T::AccountId, ExitRequestOf, OptionQuery>; + /// An extra layer of pseudo randomness. #[pallet::storage] pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; @@ -240,11 +249,13 @@ mod pallet { T: Config, { /// A juror has been added to the court. - JoinedJuror { juror: T::AccountId }, + JurorJoined { juror: T::AccountId }, /// A juror prepared to exit the court. JurorPreparedExit { juror: T::AccountId }, + /// A juror could potentially still be bonded in an active court case. + JurorMayStillBeDrawn { juror: T::AccountId }, /// A juror has been removed from the court. - ExitedJuror { juror: T::AccountId }, + JurorExited { juror: T::AccountId }, /// A juror has voted in a court. JurorVoted { market_id: MarketIdOf, juror: T::AccountId, secret: T::Hash }, /// A juror has revealed their vote. @@ -278,8 +289,6 @@ mod pallet { JurorDoesNotExists, /// On dispute or resolution, someone tried to pass a non-court market type MarketDoesNotHaveCourtMechanism, - /// No-one voted on an outcome to resolve a market - NoVotes, /// The market is not in a state where it can be disputed. MarketIsNotDisputed, /// Only jurors can reveal their votes. @@ -300,8 +309,6 @@ mod pallet { NotInAppealPeriod, /// The court is already present for this market. CourtAlreadyExists, - /// For this appeal round the backing check extrinsic was already called. - AppealAlreadyBacked, /// The caller of this extrinsic must be a randomly selected juror. OnlyDrawnJurorsCanVote, /// The amount is below the minimum required stake. @@ -368,7 +375,6 @@ mod pallet { /// Join to become a juror, who is able to get randomly selected /// for court cases according to the provided stake. /// The probability to get selected is higher the more funds are staked. - /// The amount is added to the stake-weighted pool. /// If the pool is full, the juror with the lowest stake is removed from the juror pool. /// /// # Arguments @@ -389,7 +395,6 @@ mod pallet { let mut jurors = JurorPool::::get(); if jurors.is_full() { - // remove the lowest staked juror debug_assert!( jurors .first() @@ -400,6 +405,7 @@ mod pallet { .map(|pool_item| pool_item.stake) .unwrap_or_else(>::zero) ); + // remove the lowest staked juror jurors.remove(0); } @@ -429,7 +435,7 @@ mod pallet { let juror_info = JurorInfoOf:: { stake: amount }; >::insert(&who, juror_info); - Self::deposit_event(Event::JoinedJuror { juror: who }); + Self::deposit_event(Event::JurorJoined { juror: who }); Ok(()) } @@ -485,25 +491,43 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; - - let jurors = JurorPool::::get(); ensure!( - Self::get_pool_item(&jurors, prev_juror_info.stake).is_none(), + Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake).is_none(), Error::::JurorNotPreparedToExit ); - // TODO this belongs to prepare_exit_court and this extrinsic should be called whenever a juror is removed from the pool (use state which recognizes that the call to this extrinsic is allowed) - // TODO unbounded iteration is dangerous here + let mut exit_request = + >::get(&juror).unwrap_or(ExitRequest { last_market_id: None }); + + let mut draws = { + if let Some(last) = exit_request.last_market_id { + let hashed_market_id = >::hashed_key_for(last); + >::iter_from(hashed_market_id) + } else { + >::iter() + } + }; + // ensure not drawn for any market - for (_, draws) in >::iter() { - ensure!(!draws.iter().any(|draw| draw.juror == juror), Error::::JurorStillDrawn); + for _ in 0..T::IterationLimit::get() { + if let Some((_market_id, ds)) = draws.next() { + ensure!(!ds.iter().any(|d| d.juror == juror), Error::::JurorStillDrawn); + } else { + break; + } } - Jurors::::remove(&juror); - - T::Currency::remove_lock(T::CourtLockId::get(), &juror); + if let Some((market_id, _)) = draws.next() { + exit_request.last_market_id = Some(market_id); + >::insert(&juror, exit_request); + Self::deposit_event(Event::JurorMayStillBeDrawn { juror }); + } else { + T::Currency::remove_lock(T::CourtLockId::get(), &juror); + Jurors::::remove(&juror); + >::remove(&juror); + Self::deposit_event(Event::JurorExited { juror }); + } - Self::deposit_event(Event::ExitedJuror { juror }); Ok(()) } @@ -972,9 +996,15 @@ mod pallet { .sum::(); let mut random_set = BTreeSet::new(); + let mut insert_unused_random_number = || { + // this loop is to make sure we don't insert the same random number twice + while !random_set.insert(rng.gen_range(0u128..=total_weight)) { + random_set.insert(rng.gen_range(0u128..=total_weight)); + } + }; + for _ in 0..number { - let random_number = rng.gen_range(0u128..=total_weight); - random_set.insert(random_number); + insert_unused_random_number(); } let mut selections = BTreeMap::)>::new(); @@ -1036,6 +1066,11 @@ mod pallet { // we allow at most MaxDraws jurors // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 // MaxDraws should be 191 in this case + debug_assert!( + random_jurors.len() <= T::MaxDraws::get() as usize, + "The number of randomly selected jurors should be less than or equal to \ + `MaxDraws`." + ); let draws = >::truncate_from(random_jurors); debug_assert!( if appeal_number > 0 { @@ -1149,12 +1184,12 @@ mod pallet { } else { let slash = T::RedistributionPercentage::get() * *slashable; let (imb, missing) = T::Currency::slash(juror, slash); + total_incentives.subsume(imb); debug_assert!( missing.is_zero(), "Could not slash all of the amount for juror {:?}.", juror ); - total_incentives.subsume(imb); if let Some(juror_info) = >::get(juror) { if let Some((index, _)) = Self::get_pool_item(&jurors, juror_info.stake) { diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 59fc518df..8e3849722 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -32,9 +32,9 @@ use sp_runtime::{ use zeitgeist_primitives::{ constants::mock::{ AppealBond, AppealBondFactor, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, - CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDraws, - MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, - RequestInterval, TardySlashPercentage, BASE, + CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, IterationLimit, + MaxAppeals, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, + RedistributionPercentage, RequestInterval, TardySlashPercentage, BASE, }, traits::DisputeResolutionApi, types::{ @@ -124,6 +124,7 @@ impl crate::Config for Runtime { type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = NoopResolution; type Event = (); + type IterationLimit = IterationLimit; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index a6ff47312..80181ea0a 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -31,6 +31,20 @@ pub struct JurorInfo { pub(crate) stake: Balance, } +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct ExitRequest { + pub(crate) last_market_id: Option, +} + #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index dce8e740e..b09885d50 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -40,9 +40,9 @@ use zeitgeist_primitives::{ AppealBond, AppealBondFactor, AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, ExistentialDeposit, - ExistentialDeposits, ExitFee, GetNativeCurrencyId, LiquidityMiningPalletId, MaxAppeals, - MaxApprovals, MaxAssets, MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDraws, - MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, + ExistentialDeposits, ExitFee, GetNativeCurrencyId, IterationLimit, LiquidityMiningPalletId, + MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, MaxDisputeDuration, MaxDisputes, + MaxDraws, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, @@ -288,6 +288,7 @@ impl zrml_court::Config for Runtime { type Currency = Balances; type DenounceSlashPercentage = DenounceSlashPercentage; type Event = Event; + type IterationLimit = IterationLimit; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; From aae57d45b865fd948f4604092813545f7e7caec9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 1 Mar 2023 15:09:05 +0100 Subject: [PATCH 113/368] make choose_multiple_weighted fallible --- zrml/court/src/lib.rs | 21 ++++++++++++++------- zrml/court/src/tests.rs | 8 +++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d9b0d3779..a849d59c1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -355,6 +355,8 @@ mod pallet { OnlyGlobalDisputeAppealAllowed, /// In order to back a global dispute, it has to be the last appeal (MaxAppeals reached). NeedsToBeLastAppeal, + /// The random number generation failed. + RandNumGenFailed, } #[pallet::hooks] @@ -987,7 +989,7 @@ mod pallet { jurors: &[JurorPoolItemOf], number: usize, rng: &mut R, - ) -> Vec> { + ) -> Result>, DispatchError> { let total_weight = jurors .iter() .map(|pool_item| { @@ -996,15 +998,20 @@ mod pallet { .sum::(); let mut random_set = BTreeSet::new(); - let mut insert_unused_random_number = || { + let mut insert_unused_random_number = || -> DispatchResult { + let mut count = 0u8; // this loop is to make sure we don't insert the same random number twice while !random_set.insert(rng.gen_range(0u128..=total_weight)) { - random_set.insert(rng.gen_range(0u128..=total_weight)); + count = count.saturating_add(1u8); + if count >= 3u8 { + return Err(Error::::RandNumGenFailed.into()); + } } + Ok(()) }; for _ in 0..number { - insert_unused_random_number(); + insert_unused_random_number()?; } let mut selections = BTreeMap::)>::new(); @@ -1039,7 +1046,7 @@ mod pallet { current_weight = upper_bound; } - selections + Ok(selections .into_iter() .map(|(juror, (weight, slashable))| Draw { juror, @@ -1047,7 +1054,7 @@ mod pallet { vote: Vote::Drawn, slashable, }) - .collect() + .collect()) } pub(crate) fn select_jurors( @@ -1061,7 +1068,7 @@ mod pallet { let mut rng = Self::rng(); let random_jurors = - Self::choose_multiple_weighted(jurors, necessary_jurors_number, &mut rng); + Self::choose_multiple_weighted(jurors, necessary_jurors_number, &mut rng)?; // we allow at most MaxDraws jurors // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 5dcb49199..a6dedc1b7 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -171,14 +171,15 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng); + let random_jurors = + Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); let mut at_least_one_set_is_different = false; for _ in 0..100 { setup_blocks(1); let another_set_of_random_jurors = - Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng); + Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); let mut iter = another_set_of_random_jurors.iter(); if let Some(juror) = iter.next() { @@ -204,7 +205,8 @@ fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); let mut rng = Court::rng(); - let random_jurors = Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng); + let random_jurors = + Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); for draw in random_jurors { assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.juror == draw.juror)); } From 85d74234b365bc2e9c01fc87aed693c5fad2835b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 1 Mar 2023 15:19:41 +0100 Subject: [PATCH 114/368] assert edge case --- zrml/court/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index a849d59c1..0acbf50a9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -427,7 +427,14 @@ mod pallet { Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), Err(i) => jurors .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), slashed }) - .map_err(|_| Error::::MaxJurorsReached)?, + .map_err(|_| { + debug_assert!( + false, + "This should never happen, because we are removing the lowest staked \ + juror above." + ); + Error::::MaxJurorsReached + })?, }; T::Currency::set_lock(T::CourtLockId::get(), &who, amount, WithdrawReasons::all()); From c3c5ca6baee310aa05a7c99758db614d372f988c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 2 Mar 2023 10:52:16 +0100 Subject: [PATCH 115/368] apply review suggestions --- primitives/src/traits/dispute_api.rs | 14 ++++++++------ zrml/authorized/src/lib.rs | 4 ++-- zrml/authorized/src/tests.rs | 15 ++++++--------- zrml/court/src/lib.rs | 4 ++-- zrml/court/src/tests.rs | 28 ++++++++++++++-------------- zrml/prediction-markets/src/lib.rs | 16 ++++++---------- zrml/simple-disputes/src/lib.rs | 4 ++-- zrml/simple-disputes/src/tests.rs | 8 ++++---- 8 files changed, 44 insertions(+), 49 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 3ef91f806..adc6e763f 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -57,21 +57,22 @@ pub trait DisputeApi { /// /// Returns the dispute mechanism's report if available, otherwise `None`. If `None` is /// returned, this means that the dispute could not be resolved. - fn get_resolution_outcome( + fn on_resolution( market_id: &Self::MarketId, market: &MarketOfDisputeApi, ) -> Result, DispatchError>; - /// Allow the flow of funds to the market dispute mechanism. + /// Allow the transfer of funds from the API caller to the API consumer and back. + /// This can be based on the final resolution outcome of the market. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. /// /// # Returns - /// Returns the negative imbalance which is meant to be used for the treasury. - fn maybe_pay( + /// Returns a negative imbalance back to the caller. + fn exchange( market_id: &Self::MarketId, market: &MarketOfDisputeApi, resolved_outcome: &OutcomeReport, - overall_imbalance: Self::NegativeImbalance, + amount: Self::NegativeImbalance, ) -> Result; /// Query the future resolution block of a disputed market. @@ -100,7 +101,8 @@ pub trait DisputeApi { market: &MarketOfDisputeApi, ) -> DispatchResult; - /// Called, when a market is destroyed. + /// Allow the API consumer to clear storage items of the dispute mechanism. + /// This may be called, when the dispute mechanism is no longer needed. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. fn clear(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; } diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index efdd3ae40..b47ebf31d 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -213,7 +213,7 @@ mod pallet { Ok(()) } - fn get_resolution_outcome( + fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -225,7 +225,7 @@ mod pallet { Ok(report.map(|r| r.outcome)) } - fn maybe_pay( + fn exchange( _: &Self::MarketId, market: &MarketOf, _: &OutcomeReport, diff --git a/zrml/authorized/src/tests.rs b/zrml/authorized/src/tests.rs index a49095014..4c5550000 100644 --- a/zrml/authorized/src/tests.rs +++ b/zrml/authorized/src/tests.rs @@ -157,15 +157,15 @@ fn authorize_market_outcome_fails_on_unauthorized_account() { } #[test] -fn get_resolution_outcome_fails_if_no_report_was_submitted() { +fn on_resolution_fails_if_no_report_was_submitted() { ExtBuilder::default().build().execute_with(|| { - let report = Authorized::get_resolution_outcome(&0, &market_mock::()).unwrap(); + let report = Authorized::on_resolution(&0, &market_mock::()).unwrap(); assert!(report.is_none()); }); } #[test] -fn get_resolution_outcome_removes_stored_outcomes() { +fn on_resolution_removes_stored_outcomes() { ExtBuilder::default().build().execute_with(|| { let market = market_mock::(); Markets::::insert(0, &market); @@ -174,13 +174,13 @@ fn get_resolution_outcome_removes_stored_outcomes() { 0, OutcomeReport::Scalar(2) )); - assert_ok!(Authorized::get_resolution_outcome(&0, &market)); + assert_ok!(Authorized::on_resolution(&0, &market)); assert_eq!(AuthorizedOutcomeReports::::get(0), None); }); } #[test] -fn get_resolution_outcome_returns_the_reported_outcome() { +fn on_resolution_returns_the_reported_outcome() { ExtBuilder::default().build().execute_with(|| { let market = market_mock::(); Markets::::insert(0, &market); @@ -195,10 +195,7 @@ fn get_resolution_outcome_returns_the_reported_outcome() { 0, OutcomeReport::Scalar(2) )); - assert_eq!( - Authorized::get_resolution_outcome(&0, &market).unwrap(), - Some(OutcomeReport::Scalar(2)) - ); + assert_eq!(Authorized::on_resolution(&0, &market).unwrap(), Some(OutcomeReport::Scalar(2))); }); } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b6254ff28..46eb59206 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -563,7 +563,7 @@ mod pallet { // rewarded if sided on the most voted outcome but jurors that voted second most // voted outcome (winner of the losing majority) are placed as tardy instead of // being slashed. - fn get_resolution_outcome( + fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -591,7 +591,7 @@ mod pallet { Ok(Some(first)) } - fn maybe_pay( + fn exchange( _: &Self::MarketId, market: &MarketOf, _: &OutcomeReport, diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 72aeb6b30..1ac6b9d2a 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -139,12 +139,12 @@ fn on_dispute_denies_non_court_markets() { } #[test] -fn get_resolution_outcome_denies_non_court_markets() { +fn on_resolution_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; assert_noop!( - Court::get_resolution_outcome(&0, &market), + Court::on_resolution(&0, &market), Error::::MarketDoesNotHaveCourtMechanism ); }); @@ -168,7 +168,7 @@ fn appeal_stores_jurors_that_should_vote() { // Alice is the winner, Bob is tardy and Charlie is the loser #[test] -fn get_resolution_outcome_awards_winners_and_slashes_losers() { +fn on_resolution_awards_winners_and_slashes_losers() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -179,7 +179,7 @@ fn get_resolution_outcome_awards_winners_and_slashes_losers() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(2)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(3)).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Balances::free_balance(ALICE), 998 * BASE + 3 * BASE); assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &ALICE), 2 * BASE); assert_eq!(Balances::free_balance(BOB), 996 * BASE); @@ -190,7 +190,7 @@ fn get_resolution_outcome_awards_winners_and_slashes_losers() { } #[test] -fn get_resolution_outcome_decides_market_outcome_based_on_the_majority() { +fn on_resolution_decides_market_outcome_based_on_the_majority() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -201,13 +201,13 @@ fn get_resolution_outcome_decides_market_outcome_based_on_the_majority() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let outcome = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); + let outcome = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(outcome, Some(OutcomeReport::Scalar(1))); }); } #[test] -fn get_resolution_outcome_sets_late_jurors_as_tardy() { +fn on_resolution_sets_late_jurors_as_tardy() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -215,14 +215,14 @@ fn get_resolution_outcome_sets_late_jurors_as_tardy() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(ALICE).unwrap().status, JurorStatus::Ok); assert_eq!(Jurors::::get(BOB).unwrap().status, JurorStatus::Tardy); }); } #[test] -fn get_resolution_outcome_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tardy() { +fn on_resolution_sets_jurors_that_voted_on_the_second_most_voted_outcome_as_tardy() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -233,13 +233,13 @@ fn get_resolution_outcome_sets_jurors_that_voted_on_the_second_most_voted_outcom Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(Jurors::::get(CHARLIE).unwrap().status, JurorStatus::Tardy); }); } #[test] -fn get_resolution_outcome_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { +fn on_resolution_punishes_tardy_jurors_that_failed_to_vote_a_second_time() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -248,7 +248,7 @@ fn get_resolution_outcome_punishes_tardy_jurors_that_failed_to_vote_a_second_tim Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); MarketCommons::push_market(DEFAULT_MARKET).unwrap(); Court::appeal(Origin::signed(ALICE), 0).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); let join_court_stake = 40000000000; let slash = join_court_stake / 5; assert_eq!(Balances::free_balance(Court::treasury_account_id()), INITIAL_BALANCE + slash); @@ -258,7 +258,7 @@ fn get_resolution_outcome_punishes_tardy_jurors_that_failed_to_vote_a_second_tim } #[test] -fn get_resolution_outcome_removes_requested_jurors_and_votes() { +fn on_resolution_removes_requested_jurors_and_votes() { ExtBuilder::default().build().execute_with(|| { setup_blocks(2); Court::join_court(Origin::signed(ALICE)).unwrap(); @@ -269,7 +269,7 @@ fn get_resolution_outcome_removes_requested_jurors_and_votes() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let _ = Court::get_resolution_outcome(&0, &DEFAULT_MARKET).unwrap(); + let _ = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); assert_eq!(RequestedJurors::::iter().count(), 0); assert_eq!(Votes::::iter().count(), 0); }); diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 1db7adc0d..ec04812e7 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2510,13 +2510,11 @@ mod pallet { if resolved_outcome_option.is_none() { resolved_outcome_option = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::get_resolution_outcome(market_id, market)? - } - MarketDisputeMechanism::Court => { - T::Court::get_resolution_outcome(market_id, market)? + T::Authorized::on_resolution(market_id, market)? } + MarketDisputeMechanism::Court => T::Court::on_resolution(market_id, market)?, MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::get_resolution_outcome(market_id, market)? + T::SimpleDisputes::on_resolution(market_id, market)? } }; } @@ -2583,21 +2581,19 @@ mod pallet { let mut imbalance_left = >::zero(); if let Some(disputor) = correct_disputor { - // TODO: The disputor gets at most the outsider and oracle bond. Is okay? CurrencyOf::::resolve_creating(&disputor, overall_imbalance); } else { - // TODO: The MDMs get at most the slashed dispute bond. Is okay? imbalance_left = overall_imbalance; } let remainder = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::maybe_pay(market_id, market, &resolved_outcome, imbalance_left)? + T::Authorized::exchange(market_id, market, &resolved_outcome, imbalance_left)? } MarketDisputeMechanism::Court => { - T::Court::maybe_pay(market_id, market, &resolved_outcome, imbalance_left)? + T::Court::exchange(market_id, market, &resolved_outcome, imbalance_left)? } - MarketDisputeMechanism::SimpleDisputes => T::SimpleDisputes::maybe_pay( + MarketDisputeMechanism::SimpleDisputes => T::SimpleDisputes::exchange( market_id, market, &resolved_outcome, diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 3a496dbee..5c5fcecd2 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -305,7 +305,7 @@ mod pallet { Ok(()) } - fn get_resolution_outcome( + fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, ) -> Result, DispatchError> { @@ -326,7 +326,7 @@ mod pallet { Ok(Some(last_dispute.outcome.clone())) } - fn maybe_pay( + fn exchange( market_id: &Self::MarketId, market: &MarketOf, resolved_outcome: &OutcomeReport, diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index 5480063b6..71b8d929e 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -62,19 +62,19 @@ fn on_dispute_denies_non_simple_disputes_markets() { } #[test] -fn get_resolution_outcome_denies_non_simple_disputes_markets() { +fn on_resolution_denies_non_simple_disputes_markets() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; market.dispute_mechanism = MarketDisputeMechanism::Court; assert_noop!( - SimpleDisputes::get_resolution_outcome(&0, &market), + SimpleDisputes::on_resolution(&0, &market), Error::::MarketDoesNotHaveSimpleDisputesMechanism ); }); } #[test] -fn get_resolution_outcome_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outcome() { +fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outcome() { ExtBuilder::default().build().execute_with(|| { let mut market = DEFAULT_MARKET; market.status = MarketStatus::Disputed; @@ -98,7 +98,7 @@ fn get_resolution_outcome_sets_the_last_dispute_of_disputed_markets_as_the_canon .unwrap(); Disputes::::insert(0, &disputes); assert_eq!( - &SimpleDisputes::get_resolution_outcome(&0, &market).unwrap().unwrap(), + &SimpleDisputes::on_resolution(&0, &market).unwrap().unwrap(), &disputes.last().unwrap().outcome ) }); From 5d873d218d42005cfc97e18168da4d846ad55bf5 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 2 Mar 2023 11:05:09 +0100 Subject: [PATCH 116/368] rename reserve_outcome to suggest_outcome --- zrml/prediction-markets/src/benchmarks.rs | 2 +- zrml/prediction-markets/src/tests.rs | 38 +++++++++++------------ zrml/simple-disputes/src/benchmarks.rs | 2 +- zrml/simple-disputes/src/lib.rs | 6 ++-- zrml/simple-disputes/src/tests.rs | 2 +- zrml/simple-disputes/src/weights.rs | 4 +-- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 3abf3d742..2e3a9c3b9 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -829,7 +829,7 @@ benchmarks! { let reserver: T::AccountId = account("Reserver", i, 0); ::AssetManager::deposit(Asset::Ztg, &reserver, (u128::MAX).saturated_into())?; let market_id_number: u128 = market_id.saturated_into::(); - let _ = zrml_simple_disputes::Call::::reserve_outcome { + let _ = zrml_simple_disputes::Call::::suggest_outcome { market_id: market_id_number.saturated_into(), outcome: OutcomeReport::Scalar(i.saturated_into()), }.dispatch_bypass_filter(RawOrigin::Signed(reserver.clone()).into())?; diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 9f2a5e7b0..98fa1389c 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -639,12 +639,12 @@ fn admin_destroy_market_correctly_unreserves_dispute_bonds() { )); run_to_block(grace_period + 2); assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) )); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(DAVE), 0, OutcomeReport::Categorical(1) @@ -2635,7 +2635,7 @@ fn it_allows_to_dispute_the_outcome_of_a_market() { run_to_block(dispute_at); assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) @@ -2828,7 +2828,7 @@ fn it_resolves_a_disputed_market() { let dispute_at_0 = report_at + 1; run_to_block(dispute_at_0); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(1) @@ -2837,7 +2837,7 @@ fn it_resolves_a_disputed_market() { let dispute_at_1 = report_at + 2; run_to_block(dispute_at_1); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(DAVE), 0, OutcomeReport::Categorical(0) @@ -2846,7 +2846,7 @@ fn it_resolves_a_disputed_market() { let dispute_at_2 = report_at + 3; run_to_block(dispute_at_2); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) @@ -3002,7 +3002,7 @@ fn start_global_dispute_works() { Error::::MarketDisputeMechanismNotFailed ); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), market_id, OutcomeReport::Categorical(i.saturated_into()), @@ -3629,7 +3629,7 @@ fn full_scalar_market_lifecycle() { // dispute assert_ok!(PredictionMarkets::dispute(Origin::signed(DAVE), 0)); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(DAVE), 0, OutcomeReport::Scalar(25) @@ -4253,7 +4253,7 @@ fn outsider_reports_wrong_outcome() { assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); check_reserve(&EVE, DisputeBond::get()); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(DAVE), 0, OutcomeReport::Categorical(0) @@ -4412,7 +4412,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark OutcomeReport::Categorical(0) )); assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(1) @@ -4463,7 +4463,7 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_approved_advised_ma OutcomeReport::Categorical(0) )); assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(1) @@ -4514,12 +4514,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark )); assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) @@ -4574,12 +4574,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma )); assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(CHARLIE), 0, OutcomeReport::Categorical(0) @@ -4639,12 +4639,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_permissionless_mark assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(FRED), 0, OutcomeReport::Categorical(0) @@ -4710,12 +4710,12 @@ fn on_resolution_correctly_reserves_and_unreserves_bonds_for_advised_approved_ma assert_ok!(PredictionMarkets::dispute(Origin::signed(EVE), 0,)); // EVE disputes with wrong outcome - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(EVE), 0, OutcomeReport::Categorical(1) )); - assert_ok!(SimpleDisputes::reserve_outcome( + assert_ok!(SimpleDisputes::suggest_outcome( Origin::signed(FRED), 0, OutcomeReport::Categorical(0) diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index dc97b753c..1d2a9d5e3 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -33,7 +33,7 @@ use sp_runtime::traits::{One, Saturating}; use zrml_market_commons::MarketCommonsPalletApi; benchmarks! { - reserve_outcome { + suggest_outcome { let d in 1..(T::MaxDisputes::get() - 1); let r in 1..63; let e in 1..63; diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 5c5fcecd2..a25e50294 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -185,13 +185,13 @@ mod pallet { #[pallet::call] impl Pallet { - #[pallet::weight(T::WeightInfo::reserve_outcome( + #[pallet::weight(T::WeightInfo::suggest_outcome( T::MaxDisputes::get(), CacheSize::get(), CacheSize::get(), ))] #[transactional] - pub fn reserve_outcome( + pub fn suggest_outcome( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, outcome: OutcomeReport, @@ -231,7 +231,7 @@ mod pallet { Self::deposit_event(Event::OutcomeReserved { market_id, dispute: market_dispute }); - Ok((Some(T::WeightInfo::reserve_outcome(num_disputes, r, e))).into()) + Ok((Some(T::WeightInfo::suggest_outcome(num_disputes, r, e))).into()) } } diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index 71b8d929e..6bb6d5457 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -104,4 +104,4 @@ fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outc }); } -// TODO test `reserve_outcome` functionality and API functionality +// TODO test `suggest_outcome` functionality and API functionality diff --git a/zrml/simple-disputes/src/weights.rs b/zrml/simple-disputes/src/weights.rs index c810c63cb..8d2851d61 100644 --- a/zrml/simple-disputes/src/weights.rs +++ b/zrml/simple-disputes/src/weights.rs @@ -45,7 +45,7 @@ use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within /// zrml_simple_disputes (automatically generated) pub trait WeightInfoZeitgeist { - fn reserve_outcome(d: u32, r: u32, e: u32) -> Weight; + fn suggest_outcome(d: u32, r: u32, e: u32) -> Weight; } /// Weight functions for zrml_simple_disputes (automatically generated) @@ -55,7 +55,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: SimpleDisputes Disputes (r:1 w:1) // Storage: Balances Reserves (r:1 w:1) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) - fn reserve_outcome(d: u32, r: u32, e: u32) -> Weight { + fn suggest_outcome(d: u32, r: u32, e: u32) -> Weight { Weight::from_ref_time(400_160_000) // Standard Error: 1_302_000 .saturating_add(Weight::from_ref_time(3_511_000).saturating_mul(d.into())) From 88a43dd06b10656a46cee4ad02b560eb9090e50d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 2 Mar 2023 11:45:29 +0100 Subject: [PATCH 117/368] separate resolve_disputed_market into parts --- zrml/prediction-markets/src/lib.rs | 68 +++++++++++++++++++----------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index ec04812e7..72039cb92 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2496,6 +2496,37 @@ mod pallet { market: &MarketOf, ) -> Result { let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; + + let resolved_outcome: OutcomeReport = + Self::get_resolved_outcome(market_id, market, &report.outcome)?; + + let imbalance_left = Self::settle_bonds(market_id, market, &resolved_outcome, report)?; + + let remainder = match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + T::Authorized::exchange(market_id, market, &resolved_outcome, imbalance_left)? + } + MarketDisputeMechanism::Court => { + T::Court::exchange(market_id, market, &resolved_outcome, imbalance_left)? + } + MarketDisputeMechanism::SimpleDisputes => T::SimpleDisputes::exchange( + market_id, + market, + &resolved_outcome, + imbalance_left, + )?, + }; + + T::Slash::on_unbalanced(remainder); + + Ok(resolved_outcome) + } + + fn get_resolved_outcome( + market_id: &MarketIdOf, + market: &MarketOf, + reported_outcome: &OutcomeReport, + ) -> Result { let mut resolved_outcome_option = None; #[cfg(feature = "with-global-disputes")] @@ -2519,15 +2550,19 @@ mod pallet { }; } - let resolved_outcome = - resolved_outcome_option.unwrap_or_else(|| report.outcome.clone()); + Ok(resolved_outcome_option.unwrap_or_else(|| reported_outcome.clone())) + } - // If the oracle reported right, return the OracleBond, otherwise slash it to - // pay the correct reporters. + fn settle_bonds( + market_id: &MarketIdOf, + market: &MarketOf, + resolved_outcome: &OutcomeReport, + report: &Report, + ) -> Result, DispatchError> { let mut overall_imbalance = NegativeImbalanceOf::::zero(); let report_by_oracle = report.by == market.oracle; - let is_correct = report.outcome == resolved_outcome; + let is_correct = &report.outcome == resolved_outcome; let unreserve_outsider = || -> DispatchResult { if Self::is_outsider_bond_pending(market_id, market, true) { @@ -2566,7 +2601,7 @@ mod pallet { } let mut correct_disputor = None; - if let Some(bond) = market.bonds.dispute.clone() { + if let Some(bond) = &market.bonds.dispute { if !bond.is_settled { if is_correct { let imb = Self::slash_dispute_bond(market_id, None)?; @@ -2574,7 +2609,7 @@ mod pallet { } else { // If the report outcome was wrong, the dispute was justified Self::unreserve_dispute_bond(market_id)?; - correct_disputor = Some(bond.who); + correct_disputor = Some(bond.who.clone()); } } } @@ -2586,24 +2621,7 @@ mod pallet { imbalance_left = overall_imbalance; } - let remainder = match market.dispute_mechanism { - MarketDisputeMechanism::Authorized => { - T::Authorized::exchange(market_id, market, &resolved_outcome, imbalance_left)? - } - MarketDisputeMechanism::Court => { - T::Court::exchange(market_id, market, &resolved_outcome, imbalance_left)? - } - MarketDisputeMechanism::SimpleDisputes => T::SimpleDisputes::exchange( - market_id, - market, - &resolved_outcome, - imbalance_left, - )?, - }; - - T::Slash::on_unbalanced(remainder); - - Ok(resolved_outcome) + Ok(imbalance_left) } pub fn on_resolution( From 85227842679d236a4a7af1fcfcecc3be5936f3ef Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 2 Mar 2023 13:50:39 +0100 Subject: [PATCH 118/368] remove link --- zrml/global-disputes/src/migrations.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/global-disputes/src/migrations.rs b/zrml/global-disputes/src/migrations.rs index 67a538e0e..f914827f8 100644 --- a/zrml/global-disputes/src/migrations.rs +++ b/zrml/global-disputes/src/migrations.rs @@ -83,7 +83,6 @@ impl OnRuntimeUpgrade total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); } - // Look here https://github.com/paritytech/substrate/blob/7ba1ef526fbcc224d3bd446c83ee66a83cacec49/frame/assets/src/migration.rs#L69-L79 let mut translated = 0u64; Outcomes::::translate::, OwnerInfoOf>, _>( |_key1, _key2, old_value| { From 2c8552295eab02693d04656149eff5d516989885 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 2 Mar 2023 14:22:53 +0100 Subject: [PATCH 119/368] rename doc string --- zrml/global-disputes/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index ac2d2d544..09ddd906f 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -89,7 +89,7 @@ pub enum GdStatus { Active { add_outcome_end: BlockNumber, vote_end: BlockNumber }, /// The global dispute is finished. Finished, - /// The global dispute was triggered to get destroyed. + /// The global dispute is destroyed. Destroyed, } From 54169e29cddb7b8511b2bd75adc8dd685681a2b2 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 2 Mar 2023 14:29:49 +0100 Subject: [PATCH 120/368] add Currency type to MarketCommons --- zrml/global-disputes/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 1b38ac799..e454d8233 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -113,6 +113,7 @@ mod pallet { /// To reference the market id type. type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, + Currency = Self::Currency, BlockNumber = Self::BlockNumber, >; From 09860bdd9613336504393f0dd4372dff7f18e2ad Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 6 Mar 2023 09:45:53 +0100 Subject: [PATCH 121/368] add debug asserts --- zrml/court/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 0acbf50a9..255535afd 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1020,6 +1020,7 @@ mod pallet { for _ in 0..number { insert_unused_random_number()?; } + debug_assert!(random_set.len() == number); let mut selections = BTreeMap::)>::new(); @@ -1389,8 +1390,9 @@ mod pallet { let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; for AppealInfo { backer, bond, appealed_outcome } in &court.appeals { if resolved_outcome == appealed_outcome { - let (imb, _) = + let (imb, missing) = T::Currency::slash_reserved_named(&Self::reserve_id(), backer, *bond); + debug_assert!(missing.is_zero()); overall_imbalance.subsume(imb); } else { T::Currency::unreserve_named(&Self::reserve_id(), backer, *bond); From 1d0773985c2d6933e42d5f30f4497496785211d0 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 6 Mar 2023 10:21:58 +0100 Subject: [PATCH 122/368] document types --- zrml/court/src/types.rs | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 80181ea0a..27cc88194 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -17,6 +17,7 @@ use zeitgeist_primitives::types::OutcomeReport; +/// The general information about a particular juror. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -28,9 +29,12 @@ use zeitgeist_primitives::types::OutcomeReport; Eq, )] pub struct JurorInfo { + /// The juror's amount in the stake weighted pool. + /// This amount is used to find a juror with a binary search on the pool. pub(crate) stake: Balance, } +/// The information required to finish exiting the court as a juror. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -42,9 +46,14 @@ pub struct JurorInfo { Eq, )] pub struct ExitRequest { + /// If the juror wants to exit the court + /// but there are too many elements inside the `Draws` storage item, + /// the last storage query (market id) is stored here + /// to continue the query in a second call to the `exit_court` extrinsic. pub(crate) last_market_id: Option, } +/// All possible states of a vote. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -56,12 +65,17 @@ pub struct ExitRequest { Eq, )] pub enum Vote { + /// The juror was randomly selected to vote in a specific court case. Drawn, + /// The juror casted a vote, only providing a hash, which meaning is unknown. Secret { secret: Hash }, + /// The juror revealed her raw vote, letting anyone know what she voted. Revealed { secret: Hash, outcome: OutcomeReport, salt: Hash }, + /// The juror was denounced, because she revealed her raw vote during the vote phase. Denounced { secret: Hash, outcome: OutcomeReport, salt: Hash }, } +/// The information about the lifecycle of a court case. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -73,12 +87,17 @@ pub enum Vote { Eq, )] pub struct Periods { + /// The end block of the pre-vote period. pub(crate) pre_vote_end: BlockNumber, + /// The end block of the vote period. pub(crate) vote_end: BlockNumber, + /// The end block of the aggregation period. pub(crate) aggregation_end: BlockNumber, + /// The end block of the appeal period. pub(crate) appeal_end: BlockNumber, } +/// The status of a court case. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -90,10 +109,15 @@ pub struct Periods { Eq, )] pub enum CourtStatus { + /// The court case has been started. Open, + /// The court case was closed, the winner outcome was determined. + /// `punished` indicates whether the tardy jurors were punished. + /// `reassigned` indicates whether the winning jurors got the funds from the loosers already. Closed { winner: OutcomeReport, punished: bool, reassigned: bool }, } +/// The information about an appeal for a court case. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -105,11 +129,15 @@ pub enum CourtStatus { Eq, )] pub struct AppealInfo { + /// The account which made the appeal. pub(crate) backer: AccountId, + /// The amount of funds which were locked for the appeal. pub(crate) bond: Balance, + /// The outcome which was appealed. pub(crate) appealed_outcome: OutcomeReport, } +/// The information about a court case. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -121,8 +149,11 @@ pub struct AppealInfo { Eq, )] pub struct CourtInfo { + /// The status of the court case. pub(crate) status: CourtStatus, + /// The list of all appeals. pub(crate) appeals: Appeals, + /// The information about the lifecycle of this court case. pub(crate) periods: Periods, } @@ -148,6 +179,8 @@ impl } } +/// After a juror was randomly selected to vote in a court case, +/// this information is relevant to handle the post-selection process. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -159,12 +192,19 @@ impl Eq, )] pub struct Draw { + /// The juror who was randomly selected. pub(crate) juror: AccountId, + /// The weight of the juror in this court case. + /// The higher the weight the more voice the juror has in the final winner decision. pub(crate) weight: u32, + /// The information about the vote state. pub(crate) vote: Vote, + /// The amount of funds which can be slashed for this court case. + /// This is related to a multiple of `MinStake` to mitigate Sybil attacks. pub(crate) slashable: Balance, } +/// All information related one item in the stake weighted juror pool. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -176,7 +216,12 @@ pub struct Draw { Eq, )] pub struct JurorPoolItem { + /// The key for the binary search to efficiently find the juror. + /// It has to be a unqiue key for each juror. pub(crate) stake: Balance, + /// The account which is the juror. pub(crate) juror: AccountId, + /// The actual slashed amount. This is useful to reduce the probability + /// of a juror to be selected again. pub(crate) slashed: Balance, } From b444b08c48d4923e206a91d5edd80e1089b9d325 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 6 Mar 2023 12:45:22 +0100 Subject: [PATCH 123/368] modify sort check --- zrml/court/src/lib.rs | 26 ++++++++++++++++---------- zrml/court/src/types.rs | 6 +++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 255535afd..bd549d6bb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -357,6 +357,8 @@ mod pallet { NeedsToBeLastAppeal, /// The random number generation failed. RandNumGenFailed, + /// The amount is too low to kick the lowest juror out of the stake-weighted pool. + AmountBelowLowestJuror, } #[pallet::hooks] @@ -397,16 +399,20 @@ mod pallet { let mut jurors = JurorPool::::get(); if jurors.is_full() { - debug_assert!( - jurors - .first() - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero) - <= jurors - .last() - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero) - ); + let lowest_juror = jurors + .first() + .map(|pool_item| pool_item.stake) + .unwrap_or_else(>::zero); + debug_assert!({ + let sorted = jurors.clone(); + sorted.sort_by_key(|pool_item| pool_item.stake); + jurors.len() == sorted.len() + && jurors + .iter() + .zip(sorted.iter()) + .all(|(a, b)| lowest_juror <= a.stake && a == b) + }); + ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); // remove the lowest staked juror jurors.remove(0); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 27cc88194..a79f2dd63 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -46,9 +46,9 @@ pub struct JurorInfo { Eq, )] pub struct ExitRequest { - /// If the juror wants to exit the court + /// If the juror wants to exit the court /// but there are too many elements inside the `Draws` storage item, - /// the last storage query (market id) is stored here + /// the last storage query (market id) is stored here /// to continue the query in a second call to the `exit_court` extrinsic. pub(crate) last_market_id: Option, } @@ -179,7 +179,7 @@ impl } } -/// After a juror was randomly selected to vote in a court case, +/// After a juror was randomly selected to vote in a court case, /// this information is relevant to handle the post-selection process. #[derive( parity_scale_codec::Decode, From 0e25a81affaca6eb2281e36b476d43f8cd94cf2d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 6 Mar 2023 12:52:21 +0100 Subject: [PATCH 124/368] avoid double remove --- zrml/court/src/lib.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bd549d6bb..fca745a0b 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -398,13 +398,20 @@ mod pallet { let mut jurors = JurorPool::::get(); - if jurors.is_full() { + let mut slashed = >::zero(); + if let Some(prev_juror_info) = >::get(&who) { + ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); + let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake) + .ok_or(Error::::JurorNeedsToExit)?; + slashed = pool_item.slashed; + jurors.remove(index); + } else if jurors.is_full() { let lowest_juror = jurors .first() .map(|pool_item| pool_item.stake) .unwrap_or_else(>::zero); debug_assert!({ - let sorted = jurors.clone(); + let mut sorted = jurors.clone(); sorted.sort_by_key(|pool_item| pool_item.stake); jurors.len() == sorted.len() && jurors @@ -417,15 +424,6 @@ mod pallet { jurors.remove(0); } - let mut slashed = >::zero(); - if let Some(prev_juror_info) = >::get(&who) { - ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); - let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake) - .ok_or(Error::::JurorNeedsToExit)?; - slashed = pool_item.slashed; - jurors.remove(index); - } - match jurors.binary_search_by_key(&amount, |pool_item| pool_item.stake) { // The reason for this error is that each amount has a clear juror // binary_search_by_key could otherwise return an index of an unwanted juror From 0045fdee1639c55ece16a819a817f1b924fdb723 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 6 Mar 2023 13:19:55 +0100 Subject: [PATCH 125/368] clarify docs --- zrml/court/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index fca745a0b..bf92c2b95 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -379,11 +379,12 @@ mod pallet { /// Join to become a juror, who is able to get randomly selected /// for court cases according to the provided stake. /// The probability to get selected is higher the more funds are staked. - /// If the pool is full, the juror with the lowest stake is removed from the juror pool. + /// The `amount` of this call represents the total stake of the juror. + /// If the pool is full, the lowest staked juror is removed from the juror pool. /// /// # Arguments /// - /// - `amount`: The amount associated with the joining juror. + /// - `amount`: The total stake associated with the joining juror. /// /// # Weight /// From b218d56527971ab6893e9eb42ed330efb8784d0a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 6 Mar 2023 13:31:56 +0100 Subject: [PATCH 126/368] avoid mut --- zrml/court/src/lib.rs | 45 +++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bf92c2b95..0473f90e9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -399,31 +399,34 @@ mod pallet { let mut jurors = JurorPool::::get(); - let mut slashed = >::zero(); - if let Some(prev_juror_info) = >::get(&who) { + let slashed = if let Some(prev_juror_info) = >::get(&who) { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake) .ok_or(Error::::JurorNeedsToExit)?; - slashed = pool_item.slashed; + let slashed = pool_item.slashed; jurors.remove(index); - } else if jurors.is_full() { - let lowest_juror = jurors - .first() - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero); - debug_assert!({ - let mut sorted = jurors.clone(); - sorted.sort_by_key(|pool_item| pool_item.stake); - jurors.len() == sorted.len() - && jurors - .iter() - .zip(sorted.iter()) - .all(|(a, b)| lowest_juror <= a.stake && a == b) - }); - ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); - // remove the lowest staked juror - jurors.remove(0); - } + slashed + } else { + if jurors.is_full() { + let lowest_juror = jurors + .first() + .map(|pool_item| pool_item.stake) + .unwrap_or_else(>::zero); + debug_assert!({ + let mut sorted = jurors.clone(); + sorted.sort_by_key(|pool_item| pool_item.stake); + jurors.len() == sorted.len() + && jurors + .iter() + .zip(sorted.iter()) + .all(|(a, b)| lowest_juror <= a.stake && a == b) + }); + ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); + // remove the lowest staked juror + jurors.remove(0); + } + >::zero() + }; match jurors.binary_search_by_key(&amount, |pool_item| pool_item.stake) { // The reason for this error is that each amount has a clear juror From c04d5ff12b73287b8552f2762babb80c5f79617d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 6 Mar 2023 16:59:12 +0100 Subject: [PATCH 127/368] binary search by account id --- zrml/court/src/lib.rs | 149 ++++++++++++++++++---------------------- zrml/court/src/tests.rs | 4 +- zrml/court/src/types.rs | 21 +----- 3 files changed, 71 insertions(+), 103 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 0473f90e9..3213c01ee 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -41,7 +41,7 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, - ExitRequest, JurorInfo, JurorPoolItem, Periods, Vote, + ExitRequest, JurorPoolItem, Periods, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -50,7 +50,7 @@ mod pallet { use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, - ensure, log, + ensure, pallet_prelude::{Hooks, OptionQuery, StorageMap, StorageValue, ValueQuery, Weight}, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, @@ -195,7 +195,6 @@ mod pallet { pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; - pub(crate) type JurorInfoOf = JurorInfo>; pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; pub(crate) type DrawOf = @@ -215,8 +214,7 @@ mod pallet { /// The general information about each juror. #[pallet::storage] - pub type Jurors = - StorageMap<_, Blake2_128Concat, T::AccountId, JurorInfoOf, OptionQuery>; + pub type Jurors = StorageMap<_, Blake2_128Concat, T::AccountId, (), OptionQuery>; /// If a juror wants to exit the court, this information is required for a multiblock execution. #[pallet::storage] @@ -332,9 +330,6 @@ mod pallet { VoteAlreadyDenounced, /// A juror tried to denounce herself. SelfDenounceDisallowed, - /// In order to execute the binary search efficiently - /// the join amount must be unqiue for each juror. - AmountAlreadyUsed, /// The court is not in the closed state. CourtNotClosed, /// The jurors were already reassigned. @@ -399,58 +394,57 @@ mod pallet { let mut jurors = JurorPool::::get(); - let slashed = if let Some(prev_juror_info) = >::get(&who) { - ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); - let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake) - .ok_or(Error::::JurorNeedsToExit)?; - let slashed = pool_item.slashed; - jurors.remove(index); - slashed - } else { - if jurors.is_full() { - let lowest_juror = jurors - .first() - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero); - debug_assert!({ - let mut sorted = jurors.clone(); - sorted.sort_by_key(|pool_item| pool_item.stake); - jurors.len() == sorted.len() - && jurors - .iter() - .zip(sorted.iter()) - .all(|(a, b)| lowest_juror <= a.stake && a == b) - }); - ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); - // remove the lowest staked juror - jurors.remove(0); + match jurors.binary_search_by_key(&who, |pool_item| pool_item.juror.clone()) { + Ok(i) => { + ensure!(amount > jurors[i].stake, Error::::AmountBelowLastJoin); + jurors[i].stake = amount; } - >::zero() - }; + Err(i) => { + ensure!(!>::contains_key(&who), Error::::JurorNeedsToExit); + if jurors.is_full() { + let lowest_juror = jurors + .first() + .map(|pool_item| pool_item.stake) + .unwrap_or_else(>::zero); + debug_assert!({ + let mut sorted = jurors.clone(); + sorted.sort_by_key(|pool_item| pool_item.stake); + jurors.len() == sorted.len() + && jurors + .iter() + .zip(sorted.iter()) + .all(|(a, b)| lowest_juror <= a.stake && a == b) + }); + ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); + // remove the lowest staked juror + jurors.remove(0); + } - match jurors.binary_search_by_key(&amount, |pool_item| pool_item.stake) { - // The reason for this error is that each amount has a clear juror - // binary_search_by_key could otherwise return an index of an unwanted juror - // if there are multiple jurors with the same stake - Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), - Err(i) => jurors - .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), slashed }) - .map_err(|_| { - debug_assert!( - false, - "This should never happen, because we are removing the lowest staked \ - juror above." - ); - Error::::MaxJurorsReached - })?, + jurors + .try_insert( + i, + JurorPoolItem { + stake: amount, + juror: who.clone(), + slashed: >::zero(), + }, + ) + .map_err(|_| { + debug_assert!( + false, + "This should never happen, because we are removing the lowest \ + staked juror above." + ); + Error::::MaxJurorsReached + })?; + } }; T::Currency::set_lock(T::CourtLockId::get(), &who, amount, WithdrawReasons::all()); JurorPool::::put(jurors); - let juror_info = JurorInfoOf:: { stake: amount }; - >::insert(&who, juror_info); + >::insert(&who, ()); Self::deposit_event(Event::JurorJoined { juror: who }); Ok(()) @@ -468,11 +462,11 @@ mod pallet { pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExists)?; + ensure!(>::contains_key(&who), Error::::JurorDoesNotExists); let mut jurors = JurorPool::::get(); - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + if let Some((index, _)) = Self::get_pool_item(&jurors, &who) { jurors.remove(index); >::put(jurors); } else { @@ -507,9 +501,9 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + ensure!(>::contains_key(&juror), Error::::JurorDoesNotExists); ensure!( - Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake).is_none(), + Self::get_pool_item(&JurorPool::::get(), &juror).is_none(), Error::::JurorNotPreparedToExit ); @@ -629,7 +623,7 @@ mod pallet { ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + ensure!(>::contains_key(&juror), Error::::JurorDoesNotExists); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -669,7 +663,7 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); let mut jurors = JurorPool::::get(); - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + if let Some((index, _)) = Self::get_pool_item(&jurors, &juror) { jurors.remove(index); >::put(jurors); } @@ -904,18 +898,9 @@ mod pallet { ); T::Currency::resolve_creating(&reward_pot, imbalance); - if let Some(prev_juror_info) = >::get(ai) { - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { - jurors.remove(index); - } - } else { - log::warn!( - "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ - {:?}.", - ai, - market_id - ); - debug_assert!(false); + debug_assert!(>::contains_key(ai), "Juror {:?} not found.", ai); + if let Some((index, _)) = Self::get_pool_item(&jurors, ai) { + jurors.remove(index); } }; @@ -1114,11 +1099,11 @@ mod pallet { // Returns (index, pool_item) if the stake associated with the juror was found. // It returns None otherwise. - pub(crate) fn get_pool_item( - jurors: &[JurorPoolItemOf], - stake: BalanceOf, - ) -> Option<(usize, &JurorPoolItemOf)> { - if let Ok(i) = jurors.binary_search_by_key(&stake, |pool_item| pool_item.stake) { + pub(crate) fn get_pool_item<'a, 'b>( + jurors: &'a [JurorPoolItemOf], + juror: &'b T::AccountId, + ) -> Option<(usize, &'a JurorPoolItemOf)> { + if let Ok(i) = jurors.binary_search_by_key(juror, |pool_item| pool_item.juror.clone()) { return Some((i, &jurors[i])); } // this None case can happen when the lowest bonded juror was removed (`join_court`) @@ -1214,13 +1199,13 @@ mod pallet { juror ); - if let Some(juror_info) = >::get(juror) { - if let Some((index, _)) = Self::get_pool_item(&jurors, juror_info.stake) { - jurors[index].slashed = jurors[index].slashed.saturating_add(slash); - } - } else { - log::warn!("Juror {:?} not found in Jurors storage.", juror); - debug_assert!(false); + debug_assert!( + >::contains_key(juror), + "Juror {:?} not found in Jurors storage.", + juror + ); + if let Some((index, _)) = Self::get_pool_item(&jurors, juror) { + jurors[index].slashed = jurors[index].slashed.saturating_add(slash); } } } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a6dedc1b7..921df6161 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -22,7 +22,7 @@ use crate::{ Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, }, - Error, JurorInfo, JurorPoolItem, Jurors, MarketOf, + Error, JurorPoolItem, Jurors, MarketOf, }; use frame_support::{assert_noop, assert_ok, traits::Hooks}; use pallet_balances::BalanceLock; @@ -101,7 +101,7 @@ fn join_court_successfully_stores_a_juror() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - assert_eq!(Jurors::::iter().next().unwrap(), (ALICE, JurorInfo { stake: amount })); + assert_eq!(Jurors::::iter().next().unwrap(), (ALICE, ())); }); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index a79f2dd63..2d5393264 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -17,23 +17,6 @@ use zeitgeist_primitives::types::OutcomeReport; -/// The general information about a particular juror. -#[derive( - parity_scale_codec::Decode, - parity_scale_codec::Encode, - parity_scale_codec::MaxEncodedLen, - scale_info::TypeInfo, - Clone, - Debug, - PartialEq, - Eq, -)] -pub struct JurorInfo { - /// The juror's amount in the stake weighted pool. - /// This amount is used to find a juror with a binary search on the pool. - pub(crate) stake: Balance, -} - /// The information required to finish exiting the court as a juror. #[derive( parity_scale_codec::Decode, @@ -217,10 +200,10 @@ pub struct Draw { )] pub struct JurorPoolItem { /// The key for the binary search to efficiently find the juror. - /// It has to be a unqiue key for each juror. - pub(crate) stake: Balance, /// The account which is the juror. pub(crate) juror: AccountId, + /// The stake of the juror. + pub(crate) stake: Balance, /// The actual slashed amount. This is useful to reduce the probability /// of a juror to be selected again. pub(crate) slashed: Balance, From f0cac316b1eb0a13526fd0bb10ac659b8da36a88 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 7 Mar 2023 10:05:57 +0100 Subject: [PATCH 128/368] Revert "binary search by account id" This reverts commit c04d5ff12b73287b8552f2762babb80c5f79617d. --- zrml/court/src/lib.rs | 149 ++++++++++++++++++++++------------------ zrml/court/src/tests.rs | 4 +- zrml/court/src/types.rs | 21 +++++- 3 files changed, 103 insertions(+), 71 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 3213c01ee..0473f90e9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -41,7 +41,7 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, - ExitRequest, JurorPoolItem, Periods, Vote, + ExitRequest, JurorInfo, JurorPoolItem, Periods, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -50,7 +50,7 @@ mod pallet { use core::marker::PhantomData; use frame_support::{ dispatch::DispatchResult, - ensure, + ensure, log, pallet_prelude::{Hooks, OptionQuery, StorageMap, StorageValue, ValueQuery, Weight}, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, @@ -195,6 +195,7 @@ mod pallet { pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; + pub(crate) type JurorInfoOf = JurorInfo>; pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; pub(crate) type DrawOf = @@ -214,7 +215,8 @@ mod pallet { /// The general information about each juror. #[pallet::storage] - pub type Jurors = StorageMap<_, Blake2_128Concat, T::AccountId, (), OptionQuery>; + pub type Jurors = + StorageMap<_, Blake2_128Concat, T::AccountId, JurorInfoOf, OptionQuery>; /// If a juror wants to exit the court, this information is required for a multiblock execution. #[pallet::storage] @@ -330,6 +332,9 @@ mod pallet { VoteAlreadyDenounced, /// A juror tried to denounce herself. SelfDenounceDisallowed, + /// In order to execute the binary search efficiently + /// the join amount must be unqiue for each juror. + AmountAlreadyUsed, /// The court is not in the closed state. CourtNotClosed, /// The jurors were already reassigned. @@ -394,57 +399,58 @@ mod pallet { let mut jurors = JurorPool::::get(); - match jurors.binary_search_by_key(&who, |pool_item| pool_item.juror.clone()) { - Ok(i) => { - ensure!(amount > jurors[i].stake, Error::::AmountBelowLastJoin); - jurors[i].stake = amount; + let slashed = if let Some(prev_juror_info) = >::get(&who) { + ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); + let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake) + .ok_or(Error::::JurorNeedsToExit)?; + let slashed = pool_item.slashed; + jurors.remove(index); + slashed + } else { + if jurors.is_full() { + let lowest_juror = jurors + .first() + .map(|pool_item| pool_item.stake) + .unwrap_or_else(>::zero); + debug_assert!({ + let mut sorted = jurors.clone(); + sorted.sort_by_key(|pool_item| pool_item.stake); + jurors.len() == sorted.len() + && jurors + .iter() + .zip(sorted.iter()) + .all(|(a, b)| lowest_juror <= a.stake && a == b) + }); + ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); + // remove the lowest staked juror + jurors.remove(0); } - Err(i) => { - ensure!(!>::contains_key(&who), Error::::JurorNeedsToExit); - if jurors.is_full() { - let lowest_juror = jurors - .first() - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero); - debug_assert!({ - let mut sorted = jurors.clone(); - sorted.sort_by_key(|pool_item| pool_item.stake); - jurors.len() == sorted.len() - && jurors - .iter() - .zip(sorted.iter()) - .all(|(a, b)| lowest_juror <= a.stake && a == b) - }); - ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); - // remove the lowest staked juror - jurors.remove(0); - } + >::zero() + }; - jurors - .try_insert( - i, - JurorPoolItem { - stake: amount, - juror: who.clone(), - slashed: >::zero(), - }, - ) - .map_err(|_| { - debug_assert!( - false, - "This should never happen, because we are removing the lowest \ - staked juror above." - ); - Error::::MaxJurorsReached - })?; - } + match jurors.binary_search_by_key(&amount, |pool_item| pool_item.stake) { + // The reason for this error is that each amount has a clear juror + // binary_search_by_key could otherwise return an index of an unwanted juror + // if there are multiple jurors with the same stake + Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), + Err(i) => jurors + .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), slashed }) + .map_err(|_| { + debug_assert!( + false, + "This should never happen, because we are removing the lowest staked \ + juror above." + ); + Error::::MaxJurorsReached + })?, }; T::Currency::set_lock(T::CourtLockId::get(), &who, amount, WithdrawReasons::all()); JurorPool::::put(jurors); - >::insert(&who, ()); + let juror_info = JurorInfoOf:: { stake: amount }; + >::insert(&who, juror_info); Self::deposit_event(Event::JurorJoined { juror: who }); Ok(()) @@ -462,11 +468,11 @@ mod pallet { pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(>::contains_key(&who), Error::::JurorDoesNotExists); + let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExists)?; let mut jurors = JurorPool::::get(); - if let Some((index, _)) = Self::get_pool_item(&jurors, &who) { + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { jurors.remove(index); >::put(jurors); } else { @@ -501,9 +507,9 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; - ensure!(>::contains_key(&juror), Error::::JurorDoesNotExists); + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; ensure!( - Self::get_pool_item(&JurorPool::::get(), &juror).is_none(), + Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake).is_none(), Error::::JurorNotPreparedToExit ); @@ -623,7 +629,7 @@ mod pallet { ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); - ensure!(>::contains_key(&juror), Error::::JurorDoesNotExists); + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -663,7 +669,7 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); let mut jurors = JurorPool::::get(); - if let Some((index, _)) = Self::get_pool_item(&jurors, &juror) { + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { jurors.remove(index); >::put(jurors); } @@ -898,9 +904,18 @@ mod pallet { ); T::Currency::resolve_creating(&reward_pot, imbalance); - debug_assert!(>::contains_key(ai), "Juror {:?} not found.", ai); - if let Some((index, _)) = Self::get_pool_item(&jurors, ai) { - jurors.remove(index); + if let Some(prev_juror_info) = >::get(ai) { + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + jurors.remove(index); + } + } else { + log::warn!( + "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ + {:?}.", + ai, + market_id + ); + debug_assert!(false); } }; @@ -1099,11 +1114,11 @@ mod pallet { // Returns (index, pool_item) if the stake associated with the juror was found. // It returns None otherwise. - pub(crate) fn get_pool_item<'a, 'b>( - jurors: &'a [JurorPoolItemOf], - juror: &'b T::AccountId, - ) -> Option<(usize, &'a JurorPoolItemOf)> { - if let Ok(i) = jurors.binary_search_by_key(juror, |pool_item| pool_item.juror.clone()) { + pub(crate) fn get_pool_item( + jurors: &[JurorPoolItemOf], + stake: BalanceOf, + ) -> Option<(usize, &JurorPoolItemOf)> { + if let Ok(i) = jurors.binary_search_by_key(&stake, |pool_item| pool_item.stake) { return Some((i, &jurors[i])); } // this None case can happen when the lowest bonded juror was removed (`join_court`) @@ -1199,13 +1214,13 @@ mod pallet { juror ); - debug_assert!( - >::contains_key(juror), - "Juror {:?} not found in Jurors storage.", - juror - ); - if let Some((index, _)) = Self::get_pool_item(&jurors, juror) { - jurors[index].slashed = jurors[index].slashed.saturating_add(slash); + if let Some(juror_info) = >::get(juror) { + if let Some((index, _)) = Self::get_pool_item(&jurors, juror_info.stake) { + jurors[index].slashed = jurors[index].slashed.saturating_add(slash); + } + } else { + log::warn!("Juror {:?} not found in Jurors storage.", juror); + debug_assert!(false); } } } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 921df6161..a6dedc1b7 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -22,7 +22,7 @@ use crate::{ Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, }, - Error, JurorPoolItem, Jurors, MarketOf, + Error, JurorInfo, JurorPoolItem, Jurors, MarketOf, }; use frame_support::{assert_noop, assert_ok, traits::Hooks}; use pallet_balances::BalanceLock; @@ -101,7 +101,7 @@ fn join_court_successfully_stores_a_juror() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - assert_eq!(Jurors::::iter().next().unwrap(), (ALICE, ())); + assert_eq!(Jurors::::iter().next().unwrap(), (ALICE, JurorInfo { stake: amount })); }); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 2d5393264..a79f2dd63 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -17,6 +17,23 @@ use zeitgeist_primitives::types::OutcomeReport; +/// The general information about a particular juror. +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct JurorInfo { + /// The juror's amount in the stake weighted pool. + /// This amount is used to find a juror with a binary search on the pool. + pub(crate) stake: Balance, +} + /// The information required to finish exiting the court as a juror. #[derive( parity_scale_codec::Decode, @@ -200,10 +217,10 @@ pub struct Draw { )] pub struct JurorPoolItem { /// The key for the binary search to efficiently find the juror. + /// It has to be a unqiue key for each juror. + pub(crate) stake: Balance, /// The account which is the juror. pub(crate) juror: AccountId, - /// The stake of the juror. - pub(crate) stake: Balance, /// The actual slashed amount. This is useful to reduce the probability /// of a juror to be selected again. pub(crate) slashed: Balance, From 7cdb8e02f58948722953468af1e4b1d5d2475de3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 7 Mar 2023 10:32:30 +0100 Subject: [PATCH 129/368] make pool item key unique --- zrml/court/src/lib.rs | 44 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 0473f90e9..5f6796939 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -332,9 +332,6 @@ mod pallet { VoteAlreadyDenounced, /// A juror tried to denounce herself. SelfDenounceDisallowed, - /// In order to execute the binary search efficiently - /// the join amount must be unqiue for each juror. - AmountAlreadyUsed, /// The court is not in the closed state. CourtNotClosed, /// The jurors were already reassigned. @@ -359,6 +356,8 @@ mod pallet { RandNumGenFailed, /// The amount is too low to kick the lowest juror out of the stake-weighted pool. AmountBelowLowestJuror, + /// This should not happen, because the juror account should only be once in a pool. + JurorTwiceInPool, } #[pallet::hooks] @@ -401,7 +400,7 @@ mod pallet { let slashed = if let Some(prev_juror_info) = >::get(&who) { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); - let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake) + let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) .ok_or(Error::::JurorNeedsToExit)?; let slashed = pool_item.slashed; jurors.remove(index); @@ -414,7 +413,7 @@ mod pallet { .unwrap_or_else(>::zero); debug_assert!({ let mut sorted = jurors.clone(); - sorted.sort_by_key(|pool_item| pool_item.stake); + sorted.sort_by_key(|pool_item| (pool_item.stake, pool_item.juror.clone())); jurors.len() == sorted.len() && jurors .iter() @@ -428,11 +427,10 @@ mod pallet { >::zero() }; - match jurors.binary_search_by_key(&amount, |pool_item| pool_item.stake) { - // The reason for this error is that each amount has a clear juror - // binary_search_by_key could otherwise return an index of an unwanted juror - // if there are multiple jurors with the same stake - Ok(_) => return Err(Error::::AmountAlreadyUsed.into()), + match jurors.binary_search_by_key(&(amount, &who), |pool_item| { + (pool_item.stake, &pool_item.juror) + }) { + Ok(_) => return Err(Error::::JurorTwiceInPool.into()), Err(i) => jurors .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), slashed }) .map_err(|_| { @@ -472,7 +470,7 @@ mod pallet { let mut jurors = JurorPool::::get(); - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) { jurors.remove(index); >::put(jurors); } else { @@ -509,7 +507,8 @@ mod pallet { let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; ensure!( - Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake).is_none(), + Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake, &juror) + .is_none(), Error::::JurorNotPreparedToExit ); @@ -669,7 +668,7 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); let mut jurors = JurorPool::::get(); - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake, &juror) { jurors.remove(index); >::put(jurors); } @@ -905,7 +904,9 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); if let Some(prev_juror_info) = >::get(ai) { - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake) { + if let Some((index, _)) = + Self::get_pool_item(&jurors, prev_juror_info.stake, ai) + { jurors.remove(index); } } else { @@ -1114,11 +1115,14 @@ mod pallet { // Returns (index, pool_item) if the stake associated with the juror was found. // It returns None otherwise. - pub(crate) fn get_pool_item( - jurors: &[JurorPoolItemOf], + pub(crate) fn get_pool_item<'a>( + jurors: &'a [JurorPoolItemOf], stake: BalanceOf, - ) -> Option<(usize, &JurorPoolItemOf)> { - if let Ok(i) = jurors.binary_search_by_key(&stake, |pool_item| pool_item.stake) { + juror: &T::AccountId, + ) -> Option<(usize, &'a JurorPoolItemOf)> { + if let Ok(i) = jurors.binary_search_by_key(&(stake, juror), |pool_item| { + (pool_item.stake, &pool_item.juror) + }) { return Some((i, &jurors[i])); } // this None case can happen when the lowest bonded juror was removed (`join_court`) @@ -1215,7 +1219,9 @@ mod pallet { ); if let Some(juror_info) = >::get(juror) { - if let Some((index, _)) = Self::get_pool_item(&jurors, juror_info.stake) { + if let Some((index, _)) = + Self::get_pool_item(&jurors, juror_info.stake, juror) + { jurors[index].slashed = jurors[index].slashed.saturating_add(slash); } } else { From d8d68a112cd7a331242774e60cd4ebfc243d6816 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 8 Mar 2023 15:10:45 +0100 Subject: [PATCH 130/368] use secure random number generator --- Cargo.lock | 1 + zrml/court/Cargo.toml | 1 + zrml/court/src/lib.rs | 14 +++++++------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0684752d..868480313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12918,6 +12918,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "rand 0.8.5", + "rand_chacha 0.3.1", "scale-info", "sp-io", "sp-runtime", diff --git a/zrml/court/Cargo.toml b/zrml/court/Cargo.toml index 096c6da68..c7aeffca8 100644 --- a/zrml/court/Cargo.toml +++ b/zrml/court/Cargo.toml @@ -5,6 +5,7 @@ frame-support = { branch = "moonbeam-polkadot-v0.9.29", default-features = false frame-system = { branch = "moonbeam-polkadot-v0.9.29", default-features = false, git = "https://github.com/zeitgeistpm/substrate" } parity-scale-codec = { default-features = false, features = ["derive", "max-encoded-len"], version = "3.0.0" } rand = { default-features = false, features = ["alloc", "std_rng"], version = "0.8" } +rand_chacha = { default-features = false, version = "0.3.1" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-runtime = { branch = "moonbeam-polkadot-v0.9.29", default-features = false, git = "https://github.com/zeitgeistpm/substrate" } zeitgeist-primitives = { default-features = false, path = "../../primitives" } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 5f6796939..7acd895d8 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -60,7 +60,8 @@ mod pallet { transactional, Blake2_128Concat, BoundedVec, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; - use rand::{rngs::StdRng, Rng, RngCore, SeedableRng}; + use rand::{Rng, RngCore, SeedableRng}; + use rand_chacha::ChaCha20Rng; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup, Zero}, DispatchError, Percent, SaturatedConversion, @@ -1167,8 +1168,9 @@ mod pallet { T::TreasuryPalletId::get().into_account_truncating() } - // Returns a pseudo random number generator implementation based on the seed - // provided by the `Config::Random` type and the `JurorsSelectionNonce` storage. + // Returns a cryptographically secure random number generator + // implementation based on the seed provided by the `Config::Random` type + // and the `JurorsSelectionNonce` storage. pub(crate) fn rng() -> impl RngCore { let nonce = >::mutate(|n| { let rslt = *n; @@ -1177,10 +1179,8 @@ mod pallet { }); let mut seed = [0; 32]; let (random_hash, _) = T::Random::random(&nonce.to_le_bytes()); - for (byte, el) in random_hash.as_ref().iter().copied().zip(seed.iter_mut()) { - *el = byte - } - StdRng::from_seed(seed) + seed.copy_from_slice(&random_hash.as_ref()[..32]); + ChaCha20Rng::from_seed(seed) } // Calculates the necessary number of jurors depending on the number of market appeals. From e35256f3037990b9c5c1e488d82346dcd9f0f31b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 16 Mar 2023 09:54:30 +0100 Subject: [PATCH 131/368] add tests --- zrml/court/src/lib.rs | 8 ++- zrml/court/src/mock.rs | 37 ++++++++++-- zrml/court/src/tests.rs | 128 +++++++++++++++++++++++++++++++--------- 3 files changed, 140 insertions(+), 33 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7acd895d8..9115dfe17 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -431,7 +431,13 @@ mod pallet { match jurors.binary_search_by_key(&(amount, &who), |pool_item| { (pool_item.stake, &pool_item.juror) }) { - Ok(_) => return Err(Error::::JurorTwiceInPool.into()), + Ok(_) => { + debug_assert!( + false, + "This should never happen, because we are removing the juror above." + ); + return Err(Error::::JurorTwiceInPool.into()); + } Err(i) => jurors .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), slashed }) .map_err(|_| { diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 8e3849722..c03dfd09f 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -22,7 +22,7 @@ use frame_support::{ construct_runtime, pallet_prelude::{DispatchError, Weight}, parameter_types, - traits::Everything, + traits::{Everything, Hooks}, PalletId, }; use sp_runtime::{ @@ -52,6 +52,7 @@ pub const FERDIE: AccountIdTest = 5; pub const GINA: AccountIdTest = 6; pub const HARRY: AccountIdTest = 7; pub const IAN: AccountIdTest = 8; +pub const POOR_PAUL: AccountIdTest = 9; pub const INITIAL_BALANCE: u128 = 1000 * BASE; parameter_types! { @@ -123,7 +124,7 @@ impl crate::Config for Runtime { type CourtAppealPeriod = CourtAppealPeriod; type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = NoopResolution; - type Event = (); + type Event = Event; type IterationLimit = IterationLimit; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; @@ -149,7 +150,7 @@ impl frame_system::Config for Runtime { type BlockWeights = (); type Call = Call; type DbWeight = (); - type Event = (); + type Event = Event; type Hash = Hash; type Hashing = BlakeTwo256; type Header = Header; @@ -170,7 +171,7 @@ impl pallet_balances::Config for Runtime { type AccountStore = System; type Balance = Balance; type DustRemoval = (); - type Event = (); + type Event = Event; type ExistentialDeposit = (); type MaxLocks = (); type MaxReserves = MaxReserves; @@ -221,6 +222,32 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); - t.into() + let mut t: sp_io::TestExternalities = t.into(); + // required to assert for events + t.execute_with(|| System::set_block_number(1)); + t } } + +pub fn run_to_block(n: BlockNumber) { + while System::block_number() < n { + Balances::on_finalize(System::block_number()); + RandomnessCollectiveFlip::on_finalize(System::block_number()); + Court::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + + // new block + let parent_block_hash = System::parent_hash(); + let current_digest = System::digest(); + System::initialize(&System::block_number(), &parent_block_hash, ¤t_digest); + System::on_initialize(System::block_number()); + Court::on_initialize(System::block_number()); + RandomnessCollectiveFlip::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + } +} + +pub fn run_blocks(n: BlockNumber) { + run_to_block(System::block_number() + n); +} \ No newline at end of file diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a6dedc1b7..d77fd2bc0 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -19,15 +19,18 @@ use crate::{ mock::{ - Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, - System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, + run_to_block, Balances, Court, ExtBuilder, MarketCommons, Origin, Runtime, System, ALICE, + BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, POOR_PAUL, }, - Error, JurorInfo, JurorPoolItem, Jurors, MarketOf, + Error, Event, JurorInfo, JurorPool, JurorPoolItem, Jurors, MarketOf, JurorPoolOf }; -use frame_support::{assert_noop, assert_ok, traits::Hooks}; +use frame_support::{assert_noop, assert_ok, traits::fungible::Balanced}; use pallet_balances::BalanceLock; use zeitgeist_primitives::{ - constants::{mock::CourtLockId, BASE}, + constants::{ + mock::{CourtLockId, MinJurorStake}, + BASE, + }, traits::DisputeApi, types::{ AccountIdTest, Asset, Deadlines, Market, MarketBonds, MarketCreation, @@ -97,11 +100,96 @@ fn prepare_exit_court_will_not_remove_an_unknown_juror() { } #[test] -fn join_court_successfully_stores_a_juror() { +fn join_court_successfully_stores_required_data() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; + let alice_free_balance_before = Balances::free_balance(ALICE); assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + System::assert_last_event(Event::JurorJoined { juror: ALICE }.into()); assert_eq!(Jurors::::iter().next().unwrap(), (ALICE, JurorInfo { stake: amount })); + assert_eq!(Balances::free_balance(ALICE), alice_free_balance_before); + assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); + assert_eq!( + JurorPool::::get().into_inner(), + vec![JurorPoolItem { stake: amount, juror: ALICE, slashed: 0 }] + ); + }); +} + +#[test] +fn join_court_fails_below_min_juror_stake() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let amount = min - 1; + assert_noop!( + Court::join_court(Origin::signed(ALICE), amount), + Error::::BelowMinJurorStake + ); + }); +} + +#[test] +fn join_court_fails_insufficient_amount() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let amount = min + 1; + assert_noop!( + Court::join_court(Origin::signed(POOR_PAUL), amount), + Error::::InsufficientAmount + ); + }); +} + +#[test] +fn join_court_fails_amount_below_last_join() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let last_join_amount = 2 * min; + assert_ok!(Court::join_court(Origin::signed(ALICE), last_join_amount)); + + assert_noop!( + Court::join_court(Origin::signed(ALICE), last_join_amount - 1), + Error::::AmountBelowLastJoin + ); + }); +} + +#[test] +fn join_court_fails_juror_needs_to_exit() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let amount = 2 * min; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + + assert_noop!( + Court::join_court(Origin::signed(ALICE), amount + 1), + Error::::JurorNeedsToExit + ); + }); +} + +#[test] +fn join_court_fails_amount_below_lowest_juror() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let min_amount = 2 * min; + + let max_accounts = JurorPoolOf::::bound(); + let max_amount = min_amount + max_accounts as u128; + for i in 1..=max_accounts { + let amount = max_amount - i as u128; + let _ = Balances::deposit(&(i as u128), amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(i as u128), amount)); + } + + assert!(JurorPool::::get().is_full()); + + assert_noop!( + Court::join_court(Origin::signed(0u128), min_amount - 1), + Error::::AmountBelowLowestJuror + ); }); } @@ -132,7 +220,7 @@ fn get_resolution_outcome_denies_non_court_markets() { #[test] fn appeal_stores_jurors_that_should_vote() { ExtBuilder::default().build().execute_with(|| { - setup_blocks(123); + run_to_block(123); }); } @@ -140,7 +228,7 @@ fn appeal_stores_jurors_that_should_vote() { #[test] fn get_resolution_outcome_awards_winners_and_slashes_losers() { ExtBuilder::default().build().execute_with(|| { - setup_blocks(2); + run_to_block(2); let amount_alice = 2 * BASE; let amount_bob = 3 * BASE; let amount_charlie = 4 * BASE; @@ -154,7 +242,7 @@ fn get_resolution_outcome_awards_winners_and_slashes_losers() { #[test] fn get_resolution_outcome_decides_market_outcome_based_on_the_plurality() { ExtBuilder::default().build().execute_with(|| { - setup_blocks(2); + run_to_block(2); let amount_alice = 2 * BASE; let amount_bob = 3 * BASE; let amount_charlie = 4 * BASE; @@ -168,7 +256,7 @@ fn get_resolution_outcome_decides_market_outcome_based_on_the_plurality() { #[test] fn random_jurors_returns_an_unique_different_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { - setup_blocks(123); + run_to_block(123); let mut rng = Court::rng(); let random_jurors = @@ -176,7 +264,7 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { let mut at_least_one_set_is_different = false; for _ in 0..100 { - setup_blocks(1); + run_blocks(1); let another_set_of_random_jurors = Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); @@ -203,7 +291,7 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { #[test] fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { - setup_blocks(123); + run_to_block(123); let mut rng = Court::rng(); let random_jurors = Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); @@ -216,7 +304,7 @@ fn random_jurors_returns_a_subset_of_jurors() { #[test] fn vote_will_not_accept_unknown_accounts() { ExtBuilder::default().build().execute_with(|| { - setup_blocks(123); + run_to_block(123); let amount_alice = 2 * BASE; let amount_bob = 3 * BASE; let amount_charlie = 4 * BASE; @@ -230,17 +318,3 @@ fn vote_will_not_accept_unknown_accounts() { Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); }); } - -fn setup_blocks(num_blocks: u32) { - for _ in 0..num_blocks { - let current_block_number = System::block_number() + 1; - let parent_block_hash = System::parent_hash(); - let current_digest = System::digest(); - - System::initialize(¤t_block_number, &parent_block_hash, ¤t_digest); - RandomnessCollectiveFlip::on_initialize(current_block_number); - Court::on_initialize(current_block_number); - System::finalize(); - System::set_block_number(current_block_number); - } -} From b54fb94353e903745163ed1e4eb13b3fcd9d57c7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 16 Mar 2023 15:16:20 +0100 Subject: [PATCH 132/368] add tests --- zrml/court/src/lib.rs | 8 +- zrml/court/src/mock.rs | 2 +- zrml/court/src/tests.rs | 214 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 214 insertions(+), 10 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9115dfe17..599433afb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -287,7 +287,7 @@ mod pallet { #[pallet::error] pub enum Error { /// An account id does not exist on the jurors storage. - JurorDoesNotExists, + JurorDoesNotExist, /// On dispute or resolution, someone tried to pass a non-court market type MarketDoesNotHaveCourtMechanism, /// The market is not in a state where it can be disputed. @@ -473,7 +473,7 @@ mod pallet { pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExists)?; + let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExist)?; let mut jurors = JurorPool::::get(); @@ -512,7 +512,7 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; ensure!( Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake, &juror) .is_none(), @@ -635,7 +635,7 @@ mod pallet { ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index c03dfd09f..ee4f15817 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -250,4 +250,4 @@ pub fn run_to_block(n: BlockNumber) { pub fn run_blocks(n: BlockNumber) { run_to_block(System::block_number() + n); -} \ No newline at end of file +} diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index d77fd2bc0..19b760749 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -19,10 +19,12 @@ use crate::{ mock::{ - run_to_block, Balances, Court, ExtBuilder, MarketCommons, Origin, Runtime, System, ALICE, - BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, POOR_PAUL, + run_blocks, run_to_block, Balances, Court, ExtBuilder, MarketCommons, Origin, Runtime, + System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, + POOR_PAUL, }, - Error, Event, JurorInfo, JurorPool, JurorPoolItem, Jurors, MarketOf, JurorPoolOf + AccountIdLookupOf, Error, Event, ExitRequests, JurorInfo, JurorInfoOf, JurorPool, + JurorPoolItem, JurorPoolOf, Jurors, MarketOf, }; use frame_support::{assert_noop, assert_ok, traits::fungible::Balanced}; use pallet_balances::BalanceLock; @@ -94,7 +96,7 @@ fn prepare_exit_court_will_not_remove_an_unknown_juror() { ExtBuilder::default().build().execute_with(|| { assert_noop!( Court::prepare_exit_court(Origin::signed(ALICE)), - Error::::JurorDoesNotExists + Error::::JurorDoesNotExist ); }); } @@ -116,6 +118,71 @@ fn join_court_successfully_stores_required_data() { }); } +#[test] +fn join_court_works_multiple_joins() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let amount = 2 * min; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); + assert_eq!( + JurorPool::::get().into_inner(), + vec![JurorPoolItem { stake: amount, juror: ALICE, slashed: 0 }] + ); + assert_eq!( + Jurors::::iter().collect::)>>(), + vec![(ALICE, JurorInfo { stake: amount })] + ); + + assert_ok!(Court::join_court(Origin::signed(BOB), amount)); + assert_eq!(Balances::locks(BOB), vec![the_lock(amount)]); + assert_eq!( + JurorPool::::get().into_inner(), + vec![ + JurorPoolItem { stake: amount, juror: ALICE, slashed: 0 }, + JurorPoolItem { stake: amount, juror: BOB, slashed: 0 } + ] + ); + assert_eq!( + Jurors::::iter().collect::)>>(), + vec![(BOB, JurorInfo { stake: amount }), (ALICE, JurorInfo { stake: amount })] + ); + + let higher_amount = amount + 1; + assert_ok!(Court::join_court(Origin::signed(ALICE), higher_amount)); + assert_eq!(Balances::locks(BOB), vec![the_lock(amount)]); + assert_eq!(Balances::locks(ALICE), vec![the_lock(higher_amount)]); + assert_eq!( + JurorPool::::get().into_inner(), + vec![ + JurorPoolItem { stake: amount, juror: BOB, slashed: 0 }, + JurorPoolItem { stake: higher_amount, juror: ALICE, slashed: 0 }, + ] + ); + assert_eq!( + Jurors::::iter().collect::)>>(), + vec![(BOB, JurorInfo { stake: amount }), (ALICE, JurorInfo { stake: higher_amount })] + ); + }); +} + +#[test] +fn join_court_saves_slashed_for_double_join() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let amount = 2 * min; + + let slashed = min; + Jurors::::insert(ALICE, JurorInfo { stake: amount }); + let juror_pool = vec![JurorPoolItem { stake: amount, juror: ALICE, slashed }]; + JurorPool::::put::>(juror_pool.try_into().unwrap()); + + let higher_amount = amount + 1; + assert_ok!(Court::join_court(Origin::signed(ALICE), higher_amount)); + assert_eq!(JurorPool::::get().into_inner()[0].slashed, slashed); + }); +} + #[test] fn join_court_fails_below_min_juror_stake() { ExtBuilder::default().build().execute_with(|| { @@ -185,7 +252,7 @@ fn join_court_fails_amount_below_lowest_juror() { } assert!(JurorPool::::get().is_full()); - + assert_noop!( Court::join_court(Origin::signed(0u128), min_amount - 1), Error::::AmountBelowLowestJuror @@ -193,6 +260,143 @@ fn join_court_fails_amount_below_lowest_juror() { }); } +#[test] +fn prepare_exit_court_works() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_eq!( + JurorPool::::get().into_inner(), + vec![JurorPoolItem { stake: amount, juror: ALICE, slashed: 0 }] + ); + + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + System::assert_last_event(Event::JurorPreparedExit { juror: ALICE }.into()); + assert!(JurorPool::::get().into_inner().is_empty()); + }); +} + +#[test] +fn prepare_exit_court_removes_correct_jurors() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let min_amount = 2 * min; + + let max_accounts = JurorPoolOf::::bound(); + let mut rng = rand::thread_rng(); + let mut random_numbers: Vec = (0u32..max_accounts as u32).collect(); + use rand::seq::SliceRandom; + random_numbers.shuffle(&mut rng); + let mut random_jurors = random_numbers.clone(); + random_jurors.shuffle(&mut rng); + let max_amount = min_amount + max_accounts as u128; + for i in random_numbers { + let amount = max_amount - i as u128; + let juror = random_jurors.remove(0) as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } + + // println!("JurorPool: {:?}", JurorPool::::get().into_inner()); + + for r in 0..max_accounts { + let len = JurorPool::::get().into_inner().len(); + assert!( + JurorPool::::get().into_inner().iter().any(|item| item.juror == r as u128) + ); + assert_ok!(Court::prepare_exit_court(Origin::signed(r as u128))); + assert_eq!(JurorPool::::get().into_inner().len(), len - 1); + JurorPool::::get().into_inner().iter().for_each(|item| { + assert_ne!(item.juror, r as u128); + }); + } + }); +} + +#[test] +fn prepare_exit_court_fails_juror_already_prepared_to_exit() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_eq!( + JurorPool::::get().into_inner(), + vec![JurorPoolItem { stake: amount, juror: ALICE, slashed: 0 }] + ); + + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + assert!(JurorPool::::get().into_inner().is_empty()); + + assert_noop!( + Court::prepare_exit_court(Origin::signed(ALICE)), + Error::::JurorAlreadyPreparedToExit + ); + }); +} + +#[test] +fn prepare_exit_court_fails_juror_does_not_exist() { + ExtBuilder::default().build().execute_with(|| { + assert!( + Jurors::::iter() + .collect::)>>() + .is_empty() + ); + + assert_noop!( + Court::prepare_exit_court(Origin::signed(ALICE)), + Error::::JurorDoesNotExist + ); + }); +} + +#[test] +fn exit_court_works() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + assert!(JurorPool::::get().into_inner().is_empty()); + assert!(Jurors::::get(ALICE).is_some()); + + assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); + let alice_lookup: AccountIdLookupOf = ALICE.into(); + assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); + System::assert_last_event(Event::JurorExited { juror: ALICE }.into()); + assert!( + Jurors::::iter() + .collect::)>>() + .is_empty() + ); + assert!(!ExitRequests::::contains_key(ALICE)); + assert!(Balances::locks(ALICE).is_empty()); + }); +} + +#[test] +fn exit_court_fails_juror_does_not_exist() { + ExtBuilder::default().build().execute_with(|| { + let alice_lookup: AccountIdLookupOf = ALICE.into(); + assert_noop!( + Court::exit_court(Origin::signed(ALICE), alice_lookup), + Error::::JurorDoesNotExist + ); + }); +} + +#[test] +fn exit_court_fails_juror_not_prepared_to_exit() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + let alice_lookup: AccountIdLookupOf = ALICE.into(); + assert_noop!( + Court::exit_court(Origin::signed(ALICE), alice_lookup), + Error::::JurorNotPreparedToExit + ); + }); +} + #[test] fn on_dispute_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { From f81d03da05f1c3ab75b27af6964594df2dfee6d8 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 16 Mar 2023 16:31:14 +0100 Subject: [PATCH 133/368] found bug in exit court --- zrml/court/src/tests.rs | 106 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 19b760749..3283bcc8b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -23,14 +23,16 @@ use crate::{ System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, POOR_PAUL, }, - AccountIdLookupOf, Error, Event, ExitRequests, JurorInfo, JurorInfoOf, JurorPool, + types::{Draw, Vote}, + AccountIdLookupOf, Draws, Error, Event, ExitRequests, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, JurorPoolOf, Jurors, MarketOf, }; use frame_support::{assert_noop, assert_ok, traits::fungible::Balanced}; use pallet_balances::BalanceLock; +use rand::seq::SliceRandom; use zeitgeist_primitives::{ constants::{ - mock::{CourtLockId, MinJurorStake}, + mock::{CourtLockId, IterationLimit, MinJurorStake}, BASE, }, traits::DisputeApi, @@ -285,7 +287,6 @@ fn prepare_exit_court_removes_correct_jurors() { let max_accounts = JurorPoolOf::::bound(); let mut rng = rand::thread_rng(); let mut random_numbers: Vec = (0u32..max_accounts as u32).collect(); - use rand::seq::SliceRandom; random_numbers.shuffle(&mut rng); let mut random_jurors = random_numbers.clone(); random_jurors.shuffle(&mut rng); @@ -372,6 +373,105 @@ fn exit_court_works() { }); } +#[test] +fn exit_court_fails_juror_still_drawn() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + + let mut draws = >::get(0); + draws + .try_push(Draw { juror: ALICE, weight: 0u32, vote: Vote::Drawn, slashable: 0u128 }) + .unwrap(); + >::insert(0, draws); + let alice_lookup: AccountIdLookupOf = ALICE.into(); + assert_noop!( + Court::exit_court(Origin::signed(ALICE), alice_lookup), + Error::::JurorStillDrawn + ); + }); +} + +#[test] +fn exit_court_works_over_iteration_limit() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + + let limit = IterationLimit::get(); + for i in 0..(2 * limit) { + let mut draws = >::get(i as u128); + draws + .try_push(Draw { + juror: CHARLIE, + weight: 0u32, + vote: Vote::Drawn, + slashable: 0u128, + }) + .unwrap(); + >::insert(i as u128, draws); + } + let alice_lookup: AccountIdLookupOf = ALICE.into(); + assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); + System::assert_last_event(Event::JurorMayStillBeDrawn { juror: ALICE }.into()); + let exit_request = >::get(ALICE); + + let last_query = >::iter().skip(limit as usize).next().unwrap().0; + assert_eq!(exit_request.unwrap().last_market_id, Some(last_query)); + + assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); + System::assert_last_event(Event::JurorExited { juror: ALICE }.into()); + }); +} + +#[test] +fn check_draws_iter_new_inserts_only_after_previous() { + ExtBuilder::default().build().execute_with(|| { + let limit = IterationLimit::get(); + let excess = 2 * limit; + for i in 0..excess { + let mut draws = >::get(i as u128); + draws + .try_push(Draw { + juror: CHARLIE, + weight: 0u32, + vote: Vote::Drawn, + slashable: 0u128, + }) + .unwrap(); + >::insert(i as u128, draws); + } + + let draws = >::iter().map(|(key, _)| key).collect::>(); + + let mut numbers: Vec = (excess..(excess + limit)).collect(); + let mut rng = rand::thread_rng(); + numbers.shuffle(&mut rng); + for i in numbers { + let mut draws = >::get(i as u128); + draws + .try_push(Draw { + juror: CHARLIE, + weight: 0u32, + vote: Vote::Drawn, + slashable: 0u128, + }) + .unwrap(); + >::insert(i as u128, draws); + } + + let first_key = >::iter().next().unwrap().0; + let hashed_key = >::hashed_key_for(first_key); + let new_draws = >::iter_from(hashed_key) + .map(|(key, _)| key) + .take(excess as usize) + .collect::>(); + assert_eq!(draws, new_draws); + }); +} + #[test] fn exit_court_fails_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { From e170226b49df8c4d51c1646df8d17b88b91ef38e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 17 Mar 2023 09:36:50 +0100 Subject: [PATCH 134/368] correct slashable --- zrml/court/src/lib.rs | 69 +++++++++++++++++++++-------------------- zrml/court/src/types.rs | 4 +-- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7acd895d8..695a6ace7 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -287,7 +287,7 @@ mod pallet { #[pallet::error] pub enum Error { /// An account id does not exist on the jurors storage. - JurorDoesNotExists, + JurorDoesNotExist, /// On dispute or resolution, someone tried to pass a non-court market type MarketDoesNotHaveCourtMechanism, /// The market is not in a state where it can be disputed. @@ -399,13 +399,13 @@ mod pallet { let mut jurors = JurorPool::::get(); - let slashed = if let Some(prev_juror_info) = >::get(&who) { + let total_slashable = if let Some(prev_juror_info) = >::get(&who) { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) .ok_or(Error::::JurorNeedsToExit)?; - let slashed = pool_item.slashed; + let total_slashable = pool_item.total_slashable; jurors.remove(index); - slashed + total_slashable } else { if jurors.is_full() { let lowest_juror = jurors @@ -431,9 +431,18 @@ mod pallet { match jurors.binary_search_by_key(&(amount, &who), |pool_item| { (pool_item.stake, &pool_item.juror) }) { - Ok(_) => return Err(Error::::JurorTwiceInPool.into()), + Ok(_) => { + debug_assert!( + false, + "This should never happen, because we are removing the juror above." + ); + return Err(Error::::JurorTwiceInPool.into()); + } Err(i) => jurors - .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), slashed }) + .try_insert( + i, + JurorPoolItem { stake: amount, juror: who.clone(), total_slashable }, + ) .map_err(|_| { debug_assert!( false, @@ -467,7 +476,7 @@ mod pallet { pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExists)?; + let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExist)?; let mut jurors = JurorPool::::get(); @@ -506,7 +515,7 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; ensure!( Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake, &juror) .is_none(), @@ -629,7 +638,7 @@ mod pallet { ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExists)?; + let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -788,8 +797,7 @@ mod pallet { Error::::MaxAppealsReached })?; - let jurors: JurorPoolOf = JurorPool::::get(); - Self::select_jurors(&market_id, jurors.as_slice(), appeal_number)?; + Self::select_jurors(&market_id, appeal_number)?; let last_resolve_at = court.periods.appeal_end; let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); @@ -1003,14 +1011,17 @@ mod pallet { T: Config, { pub(crate) fn choose_multiple_weighted( - jurors: &[JurorPoolItemOf], + jurors: &mut JurorPoolOf, number: usize, rng: &mut R, ) -> Result>, DispatchError> { let total_weight = jurors .iter() .map(|pool_item| { - pool_item.stake.saturating_sub(pool_item.slashed).saturated_into::() + pool_item + .stake + .saturating_sub(pool_item.total_slashable) + .saturated_into::() }) .sum::(); @@ -1035,9 +1046,9 @@ mod pallet { let mut selections = BTreeMap::)>::new(); let mut current_weight = 0u128; - for JurorPoolItem { stake, juror, slashed } in jurors { + for JurorPoolItem { stake, juror, total_slashable } in jurors.iter_mut() { let lower_bound = current_weight; - let mut remainder = stake.saturating_sub(*slashed); + let mut remainder = stake.saturating_sub(*total_slashable); let upper_bound = current_weight.saturating_add(remainder.saturated_into::()); // this always gets the lowest random number first and maybe removes it @@ -1057,6 +1068,10 @@ mod pallet { } } + if let Some((_, draw_slashable)) = selections.get_mut(juror) { + *total_slashable = total_slashable.saturating_add(*draw_slashable); + } + if random_set.is_empty() { break; } @@ -1077,16 +1092,16 @@ mod pallet { pub(crate) fn select_jurors( market_id: &MarketIdOf, - jurors: &[JurorPoolItemOf], appeal_number: usize, ) -> Result<(), DispatchError> { + let mut jurors: JurorPoolOf = JurorPool::::get(); let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); ensure!(jurors.len() >= necessary_jurors_number, Error::::NotEnoughJurors); let mut rng = Self::rng(); let random_jurors = - Self::choose_multiple_weighted(jurors, necessary_jurors_number, &mut rng)?; + Self::choose_multiple_weighted(&mut jurors, necessary_jurors_number, &mut rng)?; // we allow at most MaxDraws jurors // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 @@ -1110,6 +1125,8 @@ mod pallet { ); // new appeal round should have a fresh set of draws >::insert(market_id, draws); + // modified total_slashable for each selected juror + >::put(jurors); Ok(()) } @@ -1202,8 +1219,6 @@ mod pallet { ) { let mut total_incentives = >::zero(); - let mut jurors = JurorPool::::get(); - let mut winners = Vec::with_capacity(valid_winners_and_losers.len()); for (juror, outcome, slashable) in valid_winners_and_losers { if outcome == winner_outcome { @@ -1217,17 +1232,6 @@ mod pallet { "Could not slash all of the amount for juror {:?}.", juror ); - - if let Some(juror_info) = >::get(juror) { - if let Some((index, _)) = - Self::get_pool_item(&jurors, juror_info.stake, juror) - { - jurors[index].slashed = jurors[index].slashed.saturating_add(slash); - } - } else { - log::warn!("Juror {:?} not found in Jurors storage.", juror); - debug_assert!(false); - } } } @@ -1251,8 +1255,6 @@ mod pallet { let treasury_acc = Self::treasury_account_id(); T::Currency::resolve_creating(&treasury_acc, total_incentives); } - - >::put(jurors); } // Returns the winner of the current court round. @@ -1342,9 +1344,8 @@ mod pallet { ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); - let jurors: JurorPoolOf = JurorPool::::get(); let appeal_number = 0usize; - Self::select_jurors(market_id, jurors.as_slice(), appeal_number)?; + Self::select_jurors(market_id, appeal_number)?; let now = >::block_number(); let request_block = >::get(); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index a79f2dd63..3e64d65bc 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -221,7 +221,7 @@ pub struct JurorPoolItem { pub(crate) stake: Balance, /// The account which is the juror. pub(crate) juror: AccountId, - /// The actual slashed amount. This is useful to reduce the probability + /// The slashable amount for all random draws. This is useful to reduce the probability /// of a juror to be selected again. - pub(crate) slashed: Balance, + pub(crate) total_slashable: Balance, } From 2005d61c4b26249dc8fd5774f13fab495f96423b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 17 Mar 2023 14:40:39 +0100 Subject: [PATCH 135/368] fix exit court --- primitives/src/constants/mock.rs | 1 - runtime/battery-station/src/parameters.rs | 2 - runtime/common/src/lib.rs | 1 - runtime/zeitgeist/src/parameters.rs | 2 - zrml/court/src/lib.rs | 104 ++++++++++------------ zrml/court/src/mock.rs | 13 ++- zrml/court/src/tests.rs | 45 ++++++---- zrml/court/src/types.rs | 21 +---- zrml/prediction-markets/src/mock.rs | 7 +- 9 files changed, 87 insertions(+), 109 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 568429308..db1a0f9dd 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -27,7 +27,6 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = 34; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); - pub const IterationLimit: u32 = 100; pub const MaxAppeals: u32 = 6; pub const MaxDraws: u32 = 191; pub const MaxJurors: u32 = 1000; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 4b9042ef6..ffbd726ac 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -95,8 +95,6 @@ parameter_types! { pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); - /// The iteration limit over all markets for the randomly selected jurors. - pub const IterationLimit: u32 = 100; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 6; /// The maximum number of randomly selected jurors for a dispute. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 382efbc7f..6e34e9ca7 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -946,7 +946,6 @@ macro_rules! impl_config_traits { type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; - type IterationLimit = IterationLimit; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 132a00021..a648b6680 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -95,8 +95,6 @@ parameter_types! { pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); - /// The iteration limit over all markets for the randomly selected jurors. - pub const IterationLimit: u32 = 100; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 6; /// The maximum number of randomly selected jurors for a dispute. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 695a6ace7..ec2e6ec9a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -41,7 +41,7 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, - ExitRequest, JurorInfo, JurorPoolItem, Periods, Vote, + JurorInfo, JurorPoolItem, Periods, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -123,9 +123,6 @@ mod pallet { /// Event type Event: From> + IsType<::Event>; - #[pallet::constant] - type IterationLimit: Get; - /// Market commons type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, @@ -204,7 +201,6 @@ mod pallet { pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; - pub(crate) type ExitRequestOf = ExitRequest>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -219,11 +215,6 @@ mod pallet { pub type Jurors = StorageMap<_, Blake2_128Concat, T::AccountId, JurorInfoOf, OptionQuery>; - /// If a juror wants to exit the court, this information is required for a multiblock execution. - #[pallet::storage] - pub type ExitRequests = - StorageMap<_, Blake2_128Concat, T::AccountId, ExitRequestOf, OptionQuery>; - /// An extra layer of pseudo randomness. #[pallet::storage] pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; @@ -253,10 +244,8 @@ mod pallet { JurorJoined { juror: T::AccountId }, /// A juror prepared to exit the court. JurorPreparedExit { juror: T::AccountId }, - /// A juror could potentially still be bonded in an active court case. - JurorMayStillBeDrawn { juror: T::AccountId }, /// A juror has been removed from the court. - JurorExited { juror: T::AccountId }, + JurorExited { juror: T::AccountId, exit_amount: BalanceOf, active_lock: BalanceOf }, /// A juror has voted in a court. JurorVoted { market_id: MarketIdOf, juror: T::AccountId, secret: T::Hash }, /// A juror has revealed their vote. @@ -316,8 +305,6 @@ mod pallet { BelowMinJurorStake, /// The maximum number of possible jurors has been reached. MaxJurorsReached, - /// In order to exit the court the juror must not be randomly selected in an active appeal. - JurorStillDrawn, /// In order to exit the court the juror has to exit /// the pool first with `prepare_exit_court`. JurorNotPreparedToExit, @@ -399,13 +386,15 @@ mod pallet { let mut jurors = JurorPool::::get(); - let total_slashable = if let Some(prev_juror_info) = >::get(&who) { + let (active_lock, total_slashable) = if let Some(prev_juror_info) = + >::get(&who) + { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) .ok_or(Error::::JurorNeedsToExit)?; let total_slashable = pool_item.total_slashable; jurors.remove(index); - total_slashable + (prev_juror_info.active_lock, total_slashable) } else { if jurors.is_full() { let lowest_juror = jurors @@ -425,7 +414,7 @@ mod pallet { // remove the lowest staked juror jurors.remove(0); } - >::zero() + (>::zero(), >::zero()) }; match jurors.binary_search_by_key(&(amount, &who), |pool_item| { @@ -457,7 +446,7 @@ mod pallet { JurorPool::::put(jurors); - let juror_info = JurorInfoOf:: { stake: amount }; + let juror_info = JurorInfoOf:: { stake: amount, active_lock }; >::insert(&who, juror_info); Self::deposit_event(Event::JurorJoined { juror: who }); @@ -494,10 +483,7 @@ mod pallet { Ok(()) } - /// Remove a juror from all courts. - /// This is only possible if the juror is not part of the pool anymore - /// (with `prepare_exit_court` or was denounced, did not reveal, did not vote) - /// and the juror is not bonded in active courts anymore. + /// Remove the juror stake from resolved courts. /// /// # Arguments /// @@ -505,9 +491,7 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n * m)`, where `n` is the number of markets - /// which have active random selections in place, and `m` is the number of jurors - /// randomly selected for each market. + /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. #[pallet::weight(1_000_000_000_000)] #[transactional] pub fn exit_court(origin: OriginFor, juror: AccountIdLookupOf) -> DispatchResult { @@ -515,44 +499,33 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; + let mut prev_juror_info = + >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; ensure!( Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake, &juror) .is_none(), Error::::JurorNotPreparedToExit ); - let mut exit_request = - >::get(&juror).unwrap_or(ExitRequest { last_market_id: None }); - - let mut draws = { - if let Some(last) = exit_request.last_market_id { - let hashed_market_id = >::hashed_key_for(last); - >::iter_from(hashed_market_id) - } else { - >::iter() - } - }; - - // ensure not drawn for any market - for _ in 0..T::IterationLimit::get() { - if let Some((_market_id, ds)) = draws.next() { - ensure!(!ds.iter().any(|d| d.juror == juror), Error::::JurorStillDrawn); - } else { - break; - } - } - - if let Some((market_id, _)) = draws.next() { - exit_request.last_market_id = Some(market_id); - >::insert(&juror, exit_request); - Self::deposit_event(Event::JurorMayStillBeDrawn { juror }); - } else { + let (exit_amount, active_lock) = if prev_juror_info.active_lock.is_zero() { T::Currency::remove_lock(T::CourtLockId::get(), &juror); Jurors::::remove(&juror); - >::remove(&juror); - Self::deposit_event(Event::JurorExited { juror }); - } + (prev_juror_info.stake, >::zero()) + } else { + T::Currency::set_lock( + T::CourtLockId::get(), + &juror, + prev_juror_info.active_lock, + WithdrawReasons::all(), + ); + let active_lock = prev_juror_info.active_lock; + let exit_amount = prev_juror_info.stake.saturating_sub(active_lock); + prev_juror_info.stake = active_lock; + Jurors::::insert(&juror, prev_juror_info); + (exit_amount, active_lock) + }; + + Self::deposit_event(Event::JurorExited { juror, exit_amount, active_lock }); Ok(()) } @@ -985,6 +958,18 @@ mod pallet { let mut valid_winners_and_losers = Vec::with_capacity(draws.len()); for draw in draws { + if let Some(mut juror_info) = >::get(&draw.juror) { + juror_info.active_lock = juror_info.active_lock.saturating_sub(draw.slashable); + >::insert(&draw.juror, juror_info); + } else { + log::warn!( + "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ + {:?}.", + draw.juror, + market_id + ); + debug_assert!(false); + } if let Vote::Revealed { secret: _, outcome, salt: _ } = draw.vote { valid_winners_and_losers.push((draw.juror, outcome, draw.slashable)); } @@ -1070,6 +1055,13 @@ mod pallet { if let Some((_, draw_slashable)) = selections.get_mut(juror) { *total_slashable = total_slashable.saturating_add(*draw_slashable); + if let Some(mut juror_info) = >::get(&*juror) { + juror_info.active_lock = + juror_info.active_lock.saturating_add(*draw_slashable); + >::insert(juror, juror_info); + } else { + debug_assert!(false, "Juror should exist in the Jurors map"); + } } if random_set.is_empty() { diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 8e3849722..69cd344f4 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -32,9 +32,9 @@ use sp_runtime::{ use zeitgeist_primitives::{ constants::mock::{ AppealBond, AppealBondFactor, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, - CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, IterationLimit, - MaxAppeals, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, - RedistributionPercentage, RequestInterval, TardySlashPercentage, BASE, + CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDraws, + MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, + RequestInterval, TardySlashPercentage, BASE, }, traits::DisputeResolutionApi, types::{ @@ -123,8 +123,7 @@ impl crate::Config for Runtime { type CourtAppealPeriod = CourtAppealPeriod; type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = NoopResolution; - type Event = (); - type IterationLimit = IterationLimit; + type Event = Event; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; @@ -149,7 +148,7 @@ impl frame_system::Config for Runtime { type BlockWeights = (); type Call = Call; type DbWeight = (); - type Event = (); + type Event = Event; type Hash = Hash; type Hashing = BlakeTwo256; type Header = Header; @@ -170,7 +169,7 @@ impl pallet_balances::Config for Runtime { type AccountStore = System; type Balance = Balance; type DustRemoval = (); - type Event = (); + type Event = Event; type ExistentialDeposit = (); type MaxLocks = (); type MaxReserves = MaxReserves; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a6dedc1b7..9f0298601 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -22,7 +22,7 @@ use crate::{ Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, }, - Error, JurorInfo, JurorPoolItem, Jurors, MarketOf, + Error, JurorInfo, JurorPool, JurorPoolItem, Jurors, MarketOf }; use frame_support::{assert_noop, assert_ok, traits::Hooks}; use pallet_balances::BalanceLock; @@ -55,15 +55,15 @@ const DEFAULT_MARKET: MarketOf = Market { }; const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ - JurorPoolItem { stake: 9, juror: HARRY, slashed: 0 }, - JurorPoolItem { stake: 8, juror: IAN, slashed: 0 }, - JurorPoolItem { stake: 7, juror: ALICE, slashed: 0 }, - JurorPoolItem { stake: 6, juror: BOB, slashed: 0 }, - JurorPoolItem { stake: 5, juror: CHARLIE, slashed: 0 }, - JurorPoolItem { stake: 4, juror: DAVE, slashed: 0 }, - JurorPoolItem { stake: 3, juror: EVE, slashed: 0 }, - JurorPoolItem { stake: 2, juror: FERDIE, slashed: 0 }, - JurorPoolItem { stake: 1, juror: GINA, slashed: 0 }, + JurorPoolItem { stake: 9, juror: HARRY, total_slashable: 0 }, + JurorPoolItem { stake: 8, juror: IAN, total_slashable: 0 }, + JurorPoolItem { stake: 7, juror: ALICE, total_slashable: 0 }, + JurorPoolItem { stake: 6, juror: BOB, total_slashable: 0 }, + JurorPoolItem { stake: 5, juror: CHARLIE, total_slashable: 0 }, + JurorPoolItem { stake: 4, juror: DAVE, total_slashable: 0 }, + JurorPoolItem { stake: 3, juror: EVE, total_slashable: 0 }, + JurorPoolItem { stake: 2, juror: FERDIE, total_slashable: 0 }, + JurorPoolItem { stake: 1, juror: GINA, total_slashable: 0 }, ]; fn the_lock(amount: u128) -> BalanceLock { @@ -91,7 +91,7 @@ fn prepare_exit_court_will_not_remove_an_unknown_juror() { ExtBuilder::default().build().execute_with(|| { assert_noop!( Court::prepare_exit_court(Origin::signed(ALICE)), - Error::::JurorDoesNotExists + Error::::JurorDoesNotExist ); }); } @@ -101,7 +101,10 @@ fn join_court_successfully_stores_a_juror() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - assert_eq!(Jurors::::iter().next().unwrap(), (ALICE, JurorInfo { stake: amount })); + assert_eq!( + Jurors::::iter().next().unwrap(), + (ALICE, JurorInfo { stake: amount, active_lock: 0u128 }) + ); }); } @@ -170,16 +173,20 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); + let mut jurors = >::get(); + for pool_item in DEFAULT_SET_OF_JURORS.iter() { + jurors.try_push(pool_item.clone()).unwrap(); + } + let mut rng = Court::rng(); - let random_jurors = - Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); + let random_jurors = Court::choose_multiple_weighted(&mut jurors, 2, &mut rng).unwrap(); let mut at_least_one_set_is_different = false; for _ in 0..100 { setup_blocks(1); let another_set_of_random_jurors = - Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); + Court::choose_multiple_weighted(&mut jurors, 2, &mut rng).unwrap(); let mut iter = another_set_of_random_jurors.iter(); if let Some(juror) = iter.next() { @@ -204,9 +211,13 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { setup_blocks(123); + let mut jurors = >::get(); + for pool_item in DEFAULT_SET_OF_JURORS.iter() { + jurors.try_push(pool_item.clone()).unwrap(); + } + let mut rng = Court::rng(); - let random_jurors = - Court::choose_multiple_weighted(DEFAULT_SET_OF_JURORS, 2, &mut rng).unwrap(); + let random_jurors = Court::choose_multiple_weighted(&mut jurors, 2, &mut rng).unwrap(); for draw in random_jurors { assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.juror == draw.juror)); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 3e64d65bc..37645d83e 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -32,25 +32,8 @@ pub struct JurorInfo { /// The juror's amount in the stake weighted pool. /// This amount is used to find a juror with a binary search on the pool. pub(crate) stake: Balance, -} - -/// The information required to finish exiting the court as a juror. -#[derive( - parity_scale_codec::Decode, - parity_scale_codec::Encode, - parity_scale_codec::MaxEncodedLen, - scale_info::TypeInfo, - Clone, - Debug, - PartialEq, - Eq, -)] -pub struct ExitRequest { - /// If the juror wants to exit the court - /// but there are too many elements inside the `Draws` storage item, - /// the last storage query (market id) is stored here - /// to continue the query in a second call to the `exit_court` extrinsic. - pub(crate) last_market_id: Option, + /// The current amount of funds which are locked in courts. + pub(crate) active_lock: Balance, } /// All possible states of a vote. diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index b09885d50..dce8e740e 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -40,9 +40,9 @@ use zeitgeist_primitives::{ AppealBond, AppealBondFactor, AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, ExistentialDeposit, - ExistentialDeposits, ExitFee, GetNativeCurrencyId, IterationLimit, LiquidityMiningPalletId, - MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, MaxDisputeDuration, MaxDisputes, - MaxDraws, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, + ExistentialDeposits, ExitFee, GetNativeCurrencyId, LiquidityMiningPalletId, MaxAppeals, + MaxApprovals, MaxAssets, MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDraws, + MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, @@ -288,7 +288,6 @@ impl zrml_court::Config for Runtime { type Currency = Balances; type DenounceSlashPercentage = DenounceSlashPercentage; type Event = Event; - type IterationLimit = IterationLimit; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; From 0cc48f0f98b47eaf963caae765101570776e7d05 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 17 Mar 2023 14:45:51 +0100 Subject: [PATCH 136/368] correct tests --- zrml/court/src/tests.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 9f0298601..83115debb 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -22,7 +22,7 @@ use crate::{ Balances, Court, ExtBuilder, MarketCommons, Origin, RandomnessCollectiveFlip, Runtime, System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, }, - Error, JurorInfo, JurorPool, JurorPoolItem, Jurors, MarketOf + Error, JurorInfo, JurorPool, JurorPoolItem, Jurors, MarketOf, }; use frame_support::{assert_noop, assert_ok, traits::Hooks}; use pallet_balances::BalanceLock; @@ -175,6 +175,10 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { let mut jurors = >::get(); for pool_item in DEFAULT_SET_OF_JURORS.iter() { + >::insert( + pool_item.juror, + JurorInfo { stake: pool_item.stake, active_lock: 0u128 }, + ); jurors.try_push(pool_item.clone()).unwrap(); } @@ -213,6 +217,10 @@ fn random_jurors_returns_a_subset_of_jurors() { setup_blocks(123); let mut jurors = >::get(); for pool_item in DEFAULT_SET_OF_JURORS.iter() { + >::insert( + pool_item.juror, + JurorInfo { stake: pool_item.stake, active_lock: 0u128 }, + ); jurors.try_push(pool_item.clone()).unwrap(); } From 7f245218d998dcd95043f0c6e245c1582e007de1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 20 Mar 2023 14:28:23 +0100 Subject: [PATCH 137/368] fix bug, add tests --- zrml/court/src/lib.rs | 6 +- zrml/court/src/tests.rs | 194 ++++++++++++++++++++++++++++++---------- 2 files changed, 151 insertions(+), 49 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index ec2e6ec9a..e80d5385b 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1252,7 +1252,7 @@ mod pallet { // Returns the winner of the current court round. // If there is no element inside `draws`, returns `None`. // If the best two outcomes have the same score, returns the last court round winner. - fn get_winner( + pub(crate) fn get_winner( draws: &[DrawOf], last_winner: Option, ) -> Option { @@ -1283,8 +1283,8 @@ mod pallet { for el in iter { if el.1 > best_score.1 { - best_score = el; second_best_score = best_score; + best_score = el; } else if el.1 > second_best_score.1 { second_best_score = el; } @@ -1297,7 +1297,7 @@ mod pallet { Some(best_score.0.clone()) } - fn get_latest_resolved_outcome( + pub(crate) fn get_latest_resolved_outcome( market_id: &MarketIdOf, ) -> Result { let market = T::MarketCommons::market(market_id)?; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 510cfb113..fc0e72e6e 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -23,12 +23,14 @@ use crate::{ System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, POOR_PAUL, }, - AccountIdLookupOf, Error, Event, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, JurorPoolOf, - Jurors, MarketOf, + types::{Draw, Vote}, + AccountIdLookupOf, AppealInfo, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, + JurorPoolItem, JurorPoolOf, Jurors, MarketOf, RequestBlock, }; use frame_support::{assert_noop, assert_ok, traits::fungible::Balanced}; use pallet_balances::BalanceLock; use rand::seq::SliceRandom; +use sp_runtime::traits::{BlakeTwo256, Hash}; use zeitgeist_primitives::{ constants::{ mock::{CourtLockId, MinJurorStake}, @@ -37,11 +39,14 @@ use zeitgeist_primitives::{ traits::DisputeApi, types::{ AccountIdTest, Asset, Deadlines, Market, MarketBonds, MarketCreation, - MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, ScoringRule, + MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, + ScoringRule, }, }; use zrml_market_commons::MarketCommonsPalletApi; +const ORACLE_REPORT: OutcomeReport = OutcomeReport::Scalar(u128::MAX); + const DEFAULT_MARKET: MarketOf = Market { base_asset: Asset::Ztg, creation: MarketCreation::Permissionless, @@ -60,6 +65,29 @@ const DEFAULT_MARKET: MarketOf = Market { bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None }, }; +fn initialize_court() -> crate::MarketIdOf { + let now = >::block_number(); + >::put(now + 1); + let amount_alice = 2 * BASE; + let amount_bob = 3 * BASE; + let amount_charlie = 4 * BASE; + let amount_dave = 5 * BASE; + let amount_eve = 6 * BASE; + Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); + Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); + Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); + Court::join_court(Origin::signed(DAVE), amount_dave).unwrap(); + Court::join_court(Origin::signed(EVE), amount_eve).unwrap(); + let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + MarketCommons::mutate_market(&market_id, |market| { + market.report = Some(Report { at: 1, by: BOB, outcome: ORACLE_REPORT }); + Ok(()) + }) + .unwrap(); + Court::on_dispute(&market_id, &DEFAULT_MARKET).unwrap(); + market_id +} + const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ JurorPoolItem { stake: 9, juror: HARRY, total_slashable: 0 }, JurorPoolItem { stake: 8, juror: IAN, total_slashable: 0 }, @@ -92,16 +120,6 @@ fn exit_court_successfully_removes_a_juror_and_frees_balances() { }); } -#[test] -fn prepare_exit_court_will_not_remove_an_unknown_juror() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Court::prepare_exit_court(Origin::signed(ALICE)), - Error::::JurorDoesNotExist - ); - }); -} - #[test] fn join_court_successfully_stores_required_data() { ExtBuilder::default().build().execute_with(|| { @@ -177,19 +195,21 @@ fn join_court_works_multiple_joins() { } #[test] -fn join_court_saves_total_slashable_for_double_join() { +fn join_court_saves_total_slashable_and_active_lock_for_double_join() { ExtBuilder::default().build().execute_with(|| { let min = MinJurorStake::get(); let amount = 2 * min; let total_slashable = min; - Jurors::::insert(ALICE, JurorInfo { stake: amount, active_lock: 0u128 }); + let active_lock = min + 1; + Jurors::::insert(ALICE, JurorInfo { stake: amount, active_lock }); let juror_pool = vec![JurorPoolItem { stake: amount, juror: ALICE, total_slashable }]; JurorPool::::put::>(juror_pool.try_into().unwrap()); let higher_amount = amount + 1; assert_ok!(Court::join_court(Origin::signed(ALICE), higher_amount)); assert_eq!(JurorPool::::get().into_inner()[0].total_slashable, total_slashable); + assert_eq!(Jurors::::get(ALICE).unwrap().active_lock, active_lock); }); } @@ -359,10 +379,12 @@ fn prepare_exit_court_fails_juror_does_not_exist() { } #[test] -fn exit_court_works() { +fn exit_court_works_without_active_lock() { ExtBuilder::default().build().execute_with(|| { - let amount = 2 * BASE; + let min = MinJurorStake::get(); + let amount = 2 * min; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert!(!JurorPool::::get().into_inner().is_empty()); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); assert!(JurorPool::::get().into_inner().is_empty()); assert!(Jurors::::get(ALICE).is_some()); @@ -382,6 +404,38 @@ fn exit_court_works() { }); } +#[test] +fn exit_court_works_with_active_lock() { + ExtBuilder::default().build().execute_with(|| { + let active_lock = MinJurorStake::get(); + let amount = 3 * active_lock; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert!(!JurorPool::::get().into_inner().is_empty()); + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + assert!(JurorPool::::get().into_inner().is_empty()); + + assert_eq!( + >::get(ALICE).unwrap(), + JurorInfo { stake: amount, active_lock: 0 } + ); + // assume that `choose_multiple_weighted` has set the active_lock + >::insert(ALICE, JurorInfo { stake: amount, active_lock }); + + assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); + let alice_lookup: AccountIdLookupOf = ALICE.into(); + assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); + System::assert_last_event( + Event::JurorExited { juror: ALICE, exit_amount: amount - active_lock, active_lock } + .into(), + ); + assert_eq!( + Jurors::::get(ALICE).unwrap(), + JurorInfo { stake: active_lock, active_lock } + ); + assert_eq!(Balances::locks(ALICE), vec![the_lock(active_lock)]); + }); +} + #[test] fn exit_court_fails_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { @@ -453,17 +507,83 @@ fn get_resolution_outcome_awards_winners_and_slashes_losers() { }); } +fn prepare_draws(market_id: &crate::MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { + let mut draws = >::get(market_id); + for (i, (outcome_index, weight)) in outcomes_with_weights.iter().enumerate() { + // offset to not conflict with other jurors + let offset_i = (i + 1000) as u128; + let juror = offset_i as u128; + let salt = BlakeTwo256::hash_of(&offset_i); + let outcome = OutcomeReport::Scalar(*outcome_index); + let secret = BlakeTwo256::hash_of(&(juror.clone(), outcome.clone(), salt)); + draws + .try_push(Draw { + juror, + weight: *weight, + vote: Vote::Revealed { secret, outcome, salt }, + slashable: 0u128, + }) + .unwrap(); + } + >::insert(market_id, draws); +} + +#[test] +fn get_winner_works() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let outcomes_and_weights = + vec![(1000u128, 8), (1001u128, 5), (1002u128, 42), (1003u128, 13)]; + prepare_draws(&market_id, outcomes_and_weights); + + let draws = >::get(market_id); + let winner = Court::get_winner(&draws.as_slice(), None).unwrap(); + assert_eq!(winner, OutcomeReport::Scalar(1002u128)); + }); +} + #[test] -fn get_resolution_outcome_decides_market_outcome_based_on_the_plurality() { +fn get_winner_returns_none_for_no_revealed_draws() { ExtBuilder::default().build().execute_with(|| { - run_to_block(2); - let amount_alice = 2 * BASE; - let amount_bob = 3 * BASE; - let amount_charlie = 4 * BASE; - Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); - Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); - Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let market_id = initialize_court(); + let draws = >::get(market_id); + let winner = Court::get_winner(&draws.as_slice(), None); + assert_eq!(winner, None); + }); +} + +#[test] +fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let mut court = >::get(&market_id).unwrap(); + // create a tie of two best outcomes + let weights = vec![(1000u128, 42), (1001u128, 42)]; + let appealed_outcome = OutcomeReport::Scalar(weights.len() as u128); + prepare_draws(&market_id, weights); + court + .appeals + .try_push(AppealInfo { + backer: CHARLIE, + bond: crate::default_appeal_bond::(1usize), + appealed_outcome: appealed_outcome.clone(), + }) + .unwrap(); + >::insert(&market_id, court); + + let latest = Court::get_latest_resolved_outcome(&market_id).unwrap(); + assert_eq!(latest, appealed_outcome); + assert!(latest != ORACLE_REPORT); + }); +} + +#[test] +fn get_latest_resolved_outcome_selects_oracle_report() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + assert_eq!(market.report.unwrap().outcome, ORACLE_REPORT); + assert_eq!(Court::get_latest_resolved_outcome(&market_id).unwrap(), ORACLE_REPORT); }); } @@ -482,14 +602,14 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { } let mut rng = Court::rng(); - let random_jurors = Court::choose_multiple_weighted(&mut jurors, 2, &mut rng).unwrap(); + let random_jurors = Court::choose_multiple_weighted(&mut jurors, 3, &mut rng).unwrap(); let mut at_least_one_set_is_different = false; for _ in 0..100 { run_blocks(1); let another_set_of_random_jurors = - Court::choose_multiple_weighted(&mut jurors, 2, &mut rng).unwrap(); + Court::choose_multiple_weighted(&mut jurors, 3, &mut rng).unwrap(); let mut iter = another_set_of_random_jurors.iter(); if let Some(juror) = iter.next() { @@ -530,21 +650,3 @@ fn random_jurors_returns_a_subset_of_jurors() { } }); } - -#[test] -fn vote_will_not_accept_unknown_accounts() { - ExtBuilder::default().build().execute_with(|| { - run_to_block(123); - let amount_alice = 2 * BASE; - let amount_bob = 3 * BASE; - let amount_charlie = 4 * BASE; - let amount_eve = 5 * BASE; - let amount_dave = 6 * BASE; - Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); - Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); - Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); - Court::join_court(Origin::signed(EVE), amount_eve).unwrap(); - Court::join_court(Origin::signed(DAVE), amount_dave).unwrap(); - Court::on_dispute(&0, &DEFAULT_MARKET).unwrap(); - }); -} From 2facc325a794a18fb1b371747d1ed5bfde5f93ec Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 20 Mar 2023 15:06:32 +0100 Subject: [PATCH 138/368] fix second get_winner bug --- zrml/court/src/lib.rs | 3 ++- zrml/court/src/tests.rs | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index e80d5385b..b0fc7aab6 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1272,8 +1272,9 @@ mod pallet { let mut best_score = iter.next()?; let mut second_best_score = if let Some(second) = iter.next() { if second.1 > best_score.1 { + let new_second = best_score; best_score = second; - best_score + new_second } else { second } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index fc0e72e6e..e5daee332 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -508,7 +508,7 @@ fn get_resolution_outcome_awards_winners_and_slashes_losers() { } fn prepare_draws(market_id: &crate::MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { - let mut draws = >::get(market_id); + let mut draws: crate::DrawsOf = vec![].try_into().unwrap(); for (i, (outcome_index, weight)) in outcomes_with_weights.iter().enumerate() { // offset to not conflict with other jurors let offset_i = (i + 1000) as u128; @@ -539,6 +539,14 @@ fn get_winner_works() { let draws = >::get(market_id); let winner = Court::get_winner(&draws.as_slice(), None).unwrap(); assert_eq!(winner, OutcomeReport::Scalar(1002u128)); + + let outcomes_and_weights = + vec![(1000u128, 2), (1000u128, 4), (1001u128, 4), (1001u128, 3)]; + prepare_draws(&market_id, outcomes_and_weights); + + let draws = >::get(market_id); + let winner = Court::get_winner(&draws.as_slice(), None).unwrap(); + assert_eq!(winner, OutcomeReport::Scalar(1001u128)); }); } From 7bf8993e68f13efe7f7b81a57c1867e0be80323a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 20 Mar 2023 17:04:36 +0100 Subject: [PATCH 139/368] modify tests --- zrml/court/src/lib.rs | 18 +++++++++--------- zrml/court/src/tests.rs | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b0fc7aab6..7a4ad87bc 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1087,16 +1087,16 @@ mod pallet { appeal_number: usize, ) -> Result<(), DispatchError> { let mut jurors: JurorPoolOf = JurorPool::::get(); - let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); - ensure!(jurors.len() >= necessary_jurors_number, Error::::NotEnoughJurors); + let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); + ensure!(jurors.len() >= necessary_jurors_weight, Error::::NotEnoughJurors); let mut rng = Self::rng(); let random_jurors = - Self::choose_multiple_weighted(&mut jurors, necessary_jurors_number, &mut rng)?; + Self::choose_multiple_weighted(&mut jurors, necessary_jurors_weight, &mut rng)?; // we allow at most MaxDraws jurors - // look at `necessary_jurors_num`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 + // look at `necessary_jurors_weight`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 // MaxDraws should be 191 in this case debug_assert!( random_jurors.len() <= T::MaxDraws::get() as usize, @@ -1193,7 +1193,7 @@ mod pallet { } // Calculates the necessary number of jurors depending on the number of market appeals. - fn necessary_jurors_num(appeals_len: usize) -> usize { + pub(crate) fn necessary_jurors_weight(appeals_len: usize) -> usize { // 2^(appeals_len) * 5 + 2^(appeals_len) - 1 // MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 SUBSEQUENT_JURORS_FACTOR @@ -1440,11 +1440,11 @@ mod pallet { Some(court) => { let appeals = &court.appeals; let appeal_number = appeals.len().saturating_add(1); - let necessary_jurors_number = Self::necessary_jurors_num(appeal_number); + let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); let valid_period = Self::check_appealable_market(market_id, &court, now).is_ok(); - if appeals.is_full() || (valid_period && (jurors_len < necessary_jurors_number)) + if appeals.is_full() || (valid_period && (jurors_len < necessary_jurors_weight)) { has_failed = true; } @@ -1457,8 +1457,8 @@ mod pallet { let during_dispute_duration = report_block <= now && now < block_after_dispute_duration; - let necessary_jurors_number = Self::necessary_jurors_num(0usize); - if during_dispute_duration && jurors_len < necessary_jurors_number { + let necessary_jurors_weight = Self::necessary_jurors_weight(0usize); + if during_dispute_duration && jurors_len < necessary_jurors_weight { has_failed = true; } } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index e5daee332..90cf137dc 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -507,6 +507,40 @@ fn get_resolution_outcome_awards_winners_and_slashes_losers() { }); } +#[test] +fn choose_multiple_weighted_works() { + ExtBuilder::default().build().execute_with(|| { + let necessary_jurors_weight = Court::necessary_jurors_weight(5usize); + let mut rng = Court::rng(); + for i in 0..necessary_jurors_weight { + let amount = MinJurorStake::get() + i as u128; + let juror = i as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } + let mut jurors = JurorPool::::get(); + let random_jurors = + Court::choose_multiple_weighted(&mut jurors, necessary_jurors_weight, &mut rng) + .unwrap(); + assert_eq!( + random_jurors.iter().map(|draw| draw.weight).sum::() as usize, + necessary_jurors_weight + ); + }); +} + +#[test] +fn check_necessary_jurors_weight() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Court::necessary_jurors_weight(0usize), 5usize); + assert_eq!(Court::necessary_jurors_weight(1usize), 11usize); + assert_eq!(Court::necessary_jurors_weight(2usize), 23usize); + assert_eq!(Court::necessary_jurors_weight(3usize), 47usize); + assert_eq!(Court::necessary_jurors_weight(4usize), 95usize); + assert_eq!(Court::necessary_jurors_weight(5usize), 191usize); + }); +} + fn prepare_draws(market_id: &crate::MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { let mut draws: crate::DrawsOf = vec![].try_into().unwrap(); for (i, (outcome_index, weight)) in outcomes_with_weights.iter().enumerate() { @@ -540,8 +574,7 @@ fn get_winner_works() { let winner = Court::get_winner(&draws.as_slice(), None).unwrap(); assert_eq!(winner, OutcomeReport::Scalar(1002u128)); - let outcomes_and_weights = - vec![(1000u128, 2), (1000u128, 4), (1001u128, 4), (1001u128, 3)]; + let outcomes_and_weights = vec![(1000u128, 2), (1000u128, 4), (1001u128, 4), (1001u128, 3)]; prepare_draws(&market_id, outcomes_and_weights); let draws = >::get(market_id); From 11e44333f9849e547e117ddeb14987a20f9c824c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 21 Mar 2023 10:32:31 +0100 Subject: [PATCH 140/368] fix draw active lock bug, add tests --- zrml/court/src/lib.rs | 22 ++++++- zrml/court/src/tests.rs | 124 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 140 insertions(+), 6 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7a4ad87bc..dbdafe332 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1103,7 +1103,23 @@ mod pallet { "The number of randomly selected jurors should be less than or equal to \ `MaxDraws`." ); - let draws = >::truncate_from(random_jurors); + // keep in mind that the old draw likely contains different jurors + for old_draw in >::get(market_id) { + if let Some(mut juror_info) = >::get(&old_draw.juror) { + juror_info.active_lock = + juror_info.active_lock.saturating_sub(old_draw.slashable); + >::insert(&old_draw.juror, juror_info); + } else { + log::warn!( + "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ + {:?}.", + old_draw.juror, + market_id + ); + debug_assert!(false); + } + } + let new_draws = >::truncate_from(random_jurors); debug_assert!( if appeal_number > 0 { appeal_number == >::get(market_id).unwrap().appeals.len() @@ -1116,7 +1132,7 @@ mod pallet { last Draws)." ); // new appeal round should have a fresh set of draws - >::insert(market_id, draws); + >::insert(market_id, new_draws); // modified total_slashable for each selected juror >::put(jurors); @@ -1195,7 +1211,7 @@ mod pallet { // Calculates the necessary number of jurors depending on the number of market appeals. pub(crate) fn necessary_jurors_weight(appeals_len: usize) -> usize { // 2^(appeals_len) * 5 + 2^(appeals_len) - 1 - // MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 + // MaxAppeals - 1 (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 SUBSEQUENT_JURORS_FACTOR .saturating_pow(appeals_len as u32) .saturating_mul(INITIAL_JURORS_NUM) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 90cf137dc..c137d1f15 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -23,7 +23,7 @@ use crate::{ System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, POOR_PAUL, }, - types::{Draw, Vote}, + types::{CourtStatus, Draw, Vote}, AccountIdLookupOf, AppealInfo, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, JurorPoolOf, Jurors, MarketOf, RequestBlock, }; @@ -33,7 +33,10 @@ use rand::seq::SliceRandom; use sp_runtime::traits::{BlakeTwo256, Hash}; use zeitgeist_primitives::{ constants::{ - mock::{CourtLockId, MinJurorStake}, + mock::{ + CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, MaxAppeals, + MaxJurors, MinJurorStake, RequestInterval, + }, BASE, }, traits::DisputeApi, @@ -67,7 +70,7 @@ const DEFAULT_MARKET: MarketOf = Market { fn initialize_court() -> crate::MarketIdOf { let now = >::block_number(); - >::put(now + 1); + >::put(now + RequestInterval::get()); let amount_alice = 2 * BASE; let amount_bob = 3 * BASE; let amount_charlie = 4 * BASE; @@ -529,6 +532,121 @@ fn choose_multiple_weighted_works() { }); } +#[test] +fn select_jurors_updates_juror_total_slashable() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + for i in 0..MaxJurors::get() { + let amount = MinJurorStake::get() + i as u128; + let juror = (i + 1000) as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } + // the last appeal is reserved for global dispute backing + let appeal_number = (MaxAppeals::get() - 1) as usize; + let mut court = Courts::::get(market_id).unwrap(); + let mut number = 0u128; + while (number as usize) < appeal_number { + let appealed_outcome = OutcomeReport::Scalar(number); + court + .appeals + .try_push(AppealInfo { + backer: number, + bond: crate::default_appeal_bond::(court.appeals.len()), + appealed_outcome, + }) + .unwrap(); + number += 1; + } + Courts::::insert(market_id, court); + + let jurors = JurorPool::::get(); + let total_slashable_before = jurors.iter().map(|juror| juror.total_slashable).sum::(); + + Court::select_jurors(&market_id, appeal_number).unwrap(); + + let draws = >::get(market_id); + let total_draw_slashable = draws.iter().map(|draw| draw.slashable).sum::(); + let jurors = JurorPool::::get(); + let total_slashable_after = jurors.iter().map(|juror| juror.total_slashable).sum::(); + assert_ne!(total_slashable_before, total_slashable_after); + assert_eq!(total_slashable_before + total_draw_slashable, total_slashable_after); + }); +} + +#[test] +fn on_dispute_creates_correct_court_info() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let court = >::get(market_id).unwrap(); + let periods = court.periods; + let request_block = >::get(); + assert_eq!(periods.pre_vote_end, request_block); + assert_eq!(periods.vote_end, periods.pre_vote_end + CourtVotePeriod::get()); + assert_eq!(periods.aggregation_end, periods.vote_end + CourtAggregationPeriod::get()); + assert_eq!(periods.appeal_end, periods.aggregation_end + CourtAppealPeriod::get()); + assert_eq!(court.status, CourtStatus::Open); + assert!(court.appeals.is_empty()); + }); +} + +#[test] +fn has_failed_returns_true_for_appealable_court_too_few_jurors() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + // force empty jurors pool + >::kill(); + let market = MarketCommons::market(&market_id).unwrap(); + let court = >::get(market_id).unwrap(); + let aggregation_end = court.periods.aggregation_end; + run_to_block(aggregation_end + 1); + assert!(Court::has_failed(&market_id, &market).unwrap()); + }); +} + +#[test] +fn has_failed_returns_true_for_appealable_court_appeals_full() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + let mut court = >::get(market_id).unwrap(); + let mut number = 0u128; + while !court.appeals.is_full() { + let appealed_outcome = OutcomeReport::Scalar(number); + court + .appeals + .try_push(AppealInfo { + backer: number, + bond: crate::default_appeal_bond::(court.appeals.len()), + appealed_outcome, + }) + .unwrap(); + number += 1; + } + >::insert(market_id, court); + assert!(Court::has_failed(&market_id, &market).unwrap()); + }); +} + +#[test] +fn has_failed_returns_true_for_uninitialized_court() { + ExtBuilder::default().build().execute_with(|| { + // force empty jurors pool + >::kill(); + let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let report_block = 42; + MarketCommons::mutate_market(&market_id, |market| { + market.report = Some(Report { at: report_block, by: BOB, outcome: ORACLE_REPORT }); + Ok(()) + }) + .unwrap(); + let market = MarketCommons::market(&market_id).unwrap(); + let block_after_dispute_duration = report_block + market.deadlines.dispute_duration; + run_to_block(block_after_dispute_duration - 1); + assert!(Court::has_failed(&market_id, &market).unwrap()); + }); +} + #[test] fn check_necessary_jurors_weight() { ExtBuilder::default().build().execute_with(|| { From a7e8b1b6661109815f646bb01c8bb52e0a1c1718 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 21 Mar 2023 10:47:57 +0100 Subject: [PATCH 141/368] rename total_slashable to consumed_stake --- zrml/court/src/lib.rs | 18 +++++++------- zrml/court/src/tests.rs | 52 ++++++++++++++++++++--------------------- zrml/court/src/types.rs | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index dbdafe332..ac2228bf7 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -386,15 +386,15 @@ mod pallet { let mut jurors = JurorPool::::get(); - let (active_lock, total_slashable) = if let Some(prev_juror_info) = + let (active_lock, consumed_stake) = if let Some(prev_juror_info) = >::get(&who) { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) .ok_or(Error::::JurorNeedsToExit)?; - let total_slashable = pool_item.total_slashable; + let consumed_stake = pool_item.consumed_stake; jurors.remove(index); - (prev_juror_info.active_lock, total_slashable) + (prev_juror_info.active_lock, consumed_stake) } else { if jurors.is_full() { let lowest_juror = jurors @@ -430,7 +430,7 @@ mod pallet { Err(i) => jurors .try_insert( i, - JurorPoolItem { stake: amount, juror: who.clone(), total_slashable }, + JurorPoolItem { stake: amount, juror: who.clone(), consumed_stake }, ) .map_err(|_| { debug_assert!( @@ -1005,7 +1005,7 @@ mod pallet { .map(|pool_item| { pool_item .stake - .saturating_sub(pool_item.total_slashable) + .saturating_sub(pool_item.consumed_stake) .saturated_into::() }) .sum::(); @@ -1031,9 +1031,9 @@ mod pallet { let mut selections = BTreeMap::)>::new(); let mut current_weight = 0u128; - for JurorPoolItem { stake, juror, total_slashable } in jurors.iter_mut() { + for JurorPoolItem { stake, juror, consumed_stake } in jurors.iter_mut() { let lower_bound = current_weight; - let mut remainder = stake.saturating_sub(*total_slashable); + let mut remainder = stake.saturating_sub(*consumed_stake); let upper_bound = current_weight.saturating_add(remainder.saturated_into::()); // this always gets the lowest random number first and maybe removes it @@ -1054,7 +1054,7 @@ mod pallet { } if let Some((_, draw_slashable)) = selections.get_mut(juror) { - *total_slashable = total_slashable.saturating_add(*draw_slashable); + *consumed_stake = consumed_stake.saturating_add(*draw_slashable); if let Some(mut juror_info) = >::get(&*juror) { juror_info.active_lock = juror_info.active_lock.saturating_add(*draw_slashable); @@ -1133,7 +1133,7 @@ mod pallet { ); // new appeal round should have a fresh set of draws >::insert(market_id, new_draws); - // modified total_slashable for each selected juror + // modified consumed_stake for each selected juror >::put(jurors); Ok(()) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index c137d1f15..214fa02f7 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -92,15 +92,15 @@ fn initialize_court() -> crate::MarketIdOf { } const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ - JurorPoolItem { stake: 9, juror: HARRY, total_slashable: 0 }, - JurorPoolItem { stake: 8, juror: IAN, total_slashable: 0 }, - JurorPoolItem { stake: 7, juror: ALICE, total_slashable: 0 }, - JurorPoolItem { stake: 6, juror: BOB, total_slashable: 0 }, - JurorPoolItem { stake: 5, juror: CHARLIE, total_slashable: 0 }, - JurorPoolItem { stake: 4, juror: DAVE, total_slashable: 0 }, - JurorPoolItem { stake: 3, juror: EVE, total_slashable: 0 }, - JurorPoolItem { stake: 2, juror: FERDIE, total_slashable: 0 }, - JurorPoolItem { stake: 1, juror: GINA, total_slashable: 0 }, + JurorPoolItem { stake: 9, juror: HARRY, consumed_stake: 0 }, + JurorPoolItem { stake: 8, juror: IAN, consumed_stake: 0 }, + JurorPoolItem { stake: 7, juror: ALICE, consumed_stake: 0 }, + JurorPoolItem { stake: 6, juror: BOB, consumed_stake: 0 }, + JurorPoolItem { stake: 5, juror: CHARLIE, consumed_stake: 0 }, + JurorPoolItem { stake: 4, juror: DAVE, consumed_stake: 0 }, + JurorPoolItem { stake: 3, juror: EVE, consumed_stake: 0 }, + JurorPoolItem { stake: 2, juror: FERDIE, consumed_stake: 0 }, + JurorPoolItem { stake: 1, juror: GINA, consumed_stake: 0 }, ]; fn the_lock(amount: u128) -> BalanceLock { @@ -138,7 +138,7 @@ fn join_court_successfully_stores_required_data() { assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_eq!( JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, total_slashable: 0 }] + vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] ); }); } @@ -152,7 +152,7 @@ fn join_court_works_multiple_joins() { assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_eq!( JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, total_slashable: 0 }] + vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] ); assert_eq!( Jurors::::iter().collect::)>>(), @@ -164,8 +164,8 @@ fn join_court_works_multiple_joins() { assert_eq!( JurorPool::::get().into_inner(), vec![ - JurorPoolItem { stake: amount, juror: ALICE, total_slashable: 0 }, - JurorPoolItem { stake: amount, juror: BOB, total_slashable: 0 } + JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }, + JurorPoolItem { stake: amount, juror: BOB, consumed_stake: 0 } ] ); assert_eq!( @@ -183,8 +183,8 @@ fn join_court_works_multiple_joins() { assert_eq!( JurorPool::::get().into_inner(), vec![ - JurorPoolItem { stake: amount, juror: BOB, total_slashable: 0 }, - JurorPoolItem { stake: higher_amount, juror: ALICE, total_slashable: 0 }, + JurorPoolItem { stake: amount, juror: BOB, consumed_stake: 0 }, + JurorPoolItem { stake: higher_amount, juror: ALICE, consumed_stake: 0 }, ] ); assert_eq!( @@ -198,20 +198,20 @@ fn join_court_works_multiple_joins() { } #[test] -fn join_court_saves_total_slashable_and_active_lock_for_double_join() { +fn join_court_saves_consumed_stake_and_active_lock_for_double_join() { ExtBuilder::default().build().execute_with(|| { let min = MinJurorStake::get(); let amount = 2 * min; - let total_slashable = min; + let consumed_stake = min; let active_lock = min + 1; Jurors::::insert(ALICE, JurorInfo { stake: amount, active_lock }); - let juror_pool = vec![JurorPoolItem { stake: amount, juror: ALICE, total_slashable }]; + let juror_pool = vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake }]; JurorPool::::put::>(juror_pool.try_into().unwrap()); let higher_amount = amount + 1; assert_ok!(Court::join_court(Origin::signed(ALICE), higher_amount)); - assert_eq!(JurorPool::::get().into_inner()[0].total_slashable, total_slashable); + assert_eq!(JurorPool::::get().into_inner()[0].consumed_stake, consumed_stake); assert_eq!(Jurors::::get(ALICE).unwrap().active_lock, active_lock); }); } @@ -300,7 +300,7 @@ fn prepare_exit_court_works() { assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!( JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, total_slashable: 0 }] + vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] ); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); @@ -352,7 +352,7 @@ fn prepare_exit_court_fails_juror_already_prepared_to_exit() { assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!( JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, total_slashable: 0 }] + vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] ); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); @@ -533,7 +533,7 @@ fn choose_multiple_weighted_works() { } #[test] -fn select_jurors_updates_juror_total_slashable() { +fn select_jurors_updates_juror_consumed_stake() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); for i in 0..MaxJurors::get() { @@ -561,16 +561,16 @@ fn select_jurors_updates_juror_total_slashable() { Courts::::insert(market_id, court); let jurors = JurorPool::::get(); - let total_slashable_before = jurors.iter().map(|juror| juror.total_slashable).sum::(); + let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); Court::select_jurors(&market_id, appeal_number).unwrap(); let draws = >::get(market_id); let total_draw_slashable = draws.iter().map(|draw| draw.slashable).sum::(); let jurors = JurorPool::::get(); - let total_slashable_after = jurors.iter().map(|juror| juror.total_slashable).sum::(); - assert_ne!(total_slashable_before, total_slashable_after); - assert_eq!(total_slashable_before + total_draw_slashable, total_slashable_after); + let consumed_stake_after = jurors.iter().map(|juror| juror.consumed_stake).sum::(); + assert_ne!(consumed_stake_before, consumed_stake_after); + assert_eq!(consumed_stake_before + total_draw_slashable, consumed_stake_after); }); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 37645d83e..a9435aec1 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -206,5 +206,5 @@ pub struct JurorPoolItem { pub(crate) juror: AccountId, /// The slashable amount for all random draws. This is useful to reduce the probability /// of a juror to be selected again. - pub(crate) total_slashable: Balance, + pub(crate) consumed_stake: Balance, } From 5a710f366f48a0e4904f3496932ad301365ef4e4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 21 Mar 2023 16:35:50 +0100 Subject: [PATCH 142/368] update config parameters, tests --- primitives/src/constants/mock.rs | 9 +- runtime/battery-station/src/parameters.rs | 12 +-- runtime/zeitgeist/src/parameters.rs | 12 +-- zrml/court/src/lib.rs | 3 - zrml/court/src/tests.rs | 112 +++++++++++++--------- 5 files changed, 83 insertions(+), 65 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index db1a0f9dd..36327a227 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -19,15 +19,14 @@ parameter_types! { parameter_types! { pub const AppealBond: Balance = 5 * BASE; pub const AppealBondFactor: Balance = 2 * BASE; - pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); pub const RequestInterval: BlockNumber = 50; - pub const CourtVotePeriod: BlockNumber = 34; - pub const CourtAggregationPeriod: BlockNumber = 34; - pub const CourtAppealPeriod: BlockNumber = 34; + pub const CourtVotePeriod: BlockNumber = 32; + pub const CourtAggregationPeriod: BlockNumber = 32; + pub const CourtAppealPeriod: BlockNumber = 16; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); - pub const MaxAppeals: u32 = 6; + pub const MaxAppeals: u32 = 4; pub const MaxDraws: u32 = 191; pub const MaxJurors: u32 = 1000; pub const MinJurorStake: Balance = 50 * CENT; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index ffbd726ac..10ff26cf7 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -81,24 +81,22 @@ parameter_types! { pub const AppealBond: Balance = 5 * BASE; /// `OutcomeBond` is increased by this factor after every new appeal. pub const AppealBondFactor: Balance = 2 * BASE; - /// Duration of a single court case. - pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. pub const CourtPalletId: PalletId = COURT_PALLET_ID; /// The time in which the jurors can cast their secret vote. - pub const CourtVotePeriod: BlockNumber = 2 * BLOCKS_PER_DAY; + pub const CourtVotePeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The time in which the jurors should reveal their secret vote. - pub const CourtAggregationPeriod: BlockNumber = 2 * BLOCKS_PER_DAY; + pub const CourtAggregationPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The time in which a court case can get appealed. - pub const CourtAppealPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; + pub const CourtAppealPeriod: BlockNumber = BLOCKS_PER_DAY; /// The court lock identifier. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. - pub const MaxAppeals: u32 = 6; + pub const MaxAppeals: u32 = 4; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDraws: u32 = 191; + pub const MaxDraws: u32 = 47; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index a648b6680..67dcda0f5 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -81,24 +81,22 @@ parameter_types! { pub const AppealBond: Balance = 2000 * BASE; /// `OutcomeBond` is increased by this factor after every new appeal. pub const AppealBondFactor: Balance = 2 * BASE; - /// Duration of a single court case. - pub const CourtCaseDuration: u64 = BLOCKS_PER_DAY; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. pub const CourtPalletId: PalletId = COURT_PALLET_ID; /// The time in which the jurors can cast their secret vote. - pub const CourtVotePeriod: BlockNumber = 2 * BLOCKS_PER_DAY; + pub const CourtVotePeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The time in which the jurors should reveal their secret vote. - pub const CourtAggregationPeriod: BlockNumber = 2 * BLOCKS_PER_DAY; + pub const CourtAggregationPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The time in which a court case can get appealed. - pub const CourtAppealPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; + pub const CourtAppealPeriod: BlockNumber = BLOCKS_PER_DAY; /// The lock identifier for the court votes. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The slash percentage if a secret vote gets revealed during the voting period. pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. - pub const MaxAppeals: u32 = 6; + pub const MaxAppeals: u32 = 4; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDraws: u32 = 191; + pub const MaxDraws: u32 = 47; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index ac2228bf7..f4bc5573f 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1095,9 +1095,6 @@ mod pallet { let random_jurors = Self::choose_multiple_weighted(&mut jurors, necessary_jurors_weight, &mut rng)?; - // we allow at most MaxDraws jurors - // look at `necessary_jurors_weight`: MaxAppeals (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 - // MaxDraws should be 191 in this case debug_assert!( random_jurors.len() <= T::MaxDraws::get() as usize, "The number of randomly selected jurors should be less than or equal to \ diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 214fa02f7..390d8d85b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -91,6 +91,33 @@ fn initialize_court() -> crate::MarketIdOf { market_id } +fn fill_juror_pool() { + for i in 0..MaxJurors::get() { + let amount = MinJurorStake::get() + i as u128; + let juror = (i + 1000) as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } +} + +fn prepare_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { + let mut court = Courts::::get(market_id).unwrap(); + let mut number = 0u128; + while (number as usize) < appeal_number { + let appealed_outcome = OutcomeReport::Scalar(number); + court + .appeals + .try_push(AppealInfo { + backer: number, + bond: crate::default_appeal_bond::(court.appeals.len()), + appealed_outcome, + }) + .unwrap(); + number += 1; + } + Courts::::insert(market_id, court); +} + const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ JurorPoolItem { stake: 9, juror: HARRY, consumed_stake: 0 }, JurorPoolItem { stake: 8, juror: IAN, consumed_stake: 0 }, @@ -368,11 +395,7 @@ fn prepare_exit_court_fails_juror_already_prepared_to_exit() { #[test] fn prepare_exit_court_fails_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { - assert!( - Jurors::::iter() - .collect::)>>() - .is_empty() - ); + assert!(Jurors::::iter().next().is_none()); assert_noop!( Court::prepare_exit_court(Origin::signed(ALICE)), @@ -393,16 +416,12 @@ fn exit_court_works_without_active_lock() { assert!(Jurors::::get(ALICE).is_some()); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); - let alice_lookup: AccountIdLookupOf = ALICE.into(); + let alice_lookup: AccountIdLookupOf = ALICE; assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); System::assert_last_event( Event::JurorExited { juror: ALICE, exit_amount: amount, active_lock: 0u128 }.into(), ); - assert!( - Jurors::::iter() - .collect::)>>() - .is_empty() - ); + assert!(Jurors::::iter().next().is_none()); assert!(Balances::locks(ALICE).is_empty()); }); } @@ -425,7 +444,7 @@ fn exit_court_works_with_active_lock() { >::insert(ALICE, JurorInfo { stake: amount, active_lock }); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); - let alice_lookup: AccountIdLookupOf = ALICE.into(); + let alice_lookup: AccountIdLookupOf = ALICE; assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); System::assert_last_event( Event::JurorExited { juror: ALICE, exit_amount: amount - active_lock, active_lock } @@ -442,7 +461,7 @@ fn exit_court_works_with_active_lock() { #[test] fn exit_court_fails_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { - let alice_lookup: AccountIdLookupOf = ALICE.into(); + let alice_lookup: AccountIdLookupOf = ALICE; assert_noop!( Court::exit_court(Origin::signed(ALICE), alice_lookup), Error::::JurorDoesNotExist @@ -456,7 +475,7 @@ fn exit_court_fails_juror_not_prepared_to_exit() { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - let alice_lookup: AccountIdLookupOf = ALICE.into(); + let alice_lookup: AccountIdLookupOf = ALICE; assert_noop!( Court::exit_court(Origin::signed(ALICE), alice_lookup), Error::::JurorNotPreparedToExit @@ -536,29 +555,10 @@ fn choose_multiple_weighted_works() { fn select_jurors_updates_juror_consumed_stake() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); - for i in 0..MaxJurors::get() { - let amount = MinJurorStake::get() + i as u128; - let juror = (i + 1000) as u128; - let _ = Balances::deposit(&juror, amount).unwrap(); - assert_ok!(Court::join_court(Origin::signed(juror), amount)); - } + fill_juror_pool(); // the last appeal is reserved for global dispute backing let appeal_number = (MaxAppeals::get() - 1) as usize; - let mut court = Courts::::get(market_id).unwrap(); - let mut number = 0u128; - while (number as usize) < appeal_number { - let appealed_outcome = OutcomeReport::Scalar(number); - court - .appeals - .try_push(AppealInfo { - backer: number, - bond: crate::default_appeal_bond::(court.appeals.len()), - appealed_outcome, - }) - .unwrap(); - number += 1; - } - Courts::::insert(market_id, court); + prepare_appeals(&market_id, appeal_number); let jurors = JurorPool::::get(); let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); @@ -574,6 +574,34 @@ fn select_jurors_updates_juror_consumed_stake() { }); } +#[test] +fn select_jurors_reduces_active_lock_for_old_draw() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + fill_juror_pool(); + let appeal_number = 1usize; + prepare_appeals(&market_id, appeal_number); + + let old_draws = >::get(market_id); + assert!(!old_draws.is_empty()); + old_draws.iter().for_each(|draw| { + let juror = draw.juror; + let juror_info = >::get(juror).unwrap(); + assert_ne!(juror_info.active_lock, 0); + assert_eq!(juror_info.active_lock, draw.slashable); + }); + + Court::select_jurors(&market_id, appeal_number).unwrap(); + + old_draws.iter().for_each(|draw| { + let juror = draw.juror; + let juror_info = >::get(juror).unwrap(); + assert_ne!(juror_info.active_lock, draw.slashable); + assert_eq!(juror_info.active_lock, 0); + }); + }); +} + #[test] fn on_dispute_creates_correct_court_info() { ExtBuilder::default().build().execute_with(|| { @@ -654,8 +682,6 @@ fn check_necessary_jurors_weight() { assert_eq!(Court::necessary_jurors_weight(1usize), 11usize); assert_eq!(Court::necessary_jurors_weight(2usize), 23usize); assert_eq!(Court::necessary_jurors_weight(3usize), 47usize); - assert_eq!(Court::necessary_jurors_weight(4usize), 95usize); - assert_eq!(Court::necessary_jurors_weight(5usize), 191usize); }); } @@ -667,7 +693,7 @@ fn prepare_draws(market_id: &crate::MarketIdOf, outcomes_with_weights: let juror = offset_i as u128; let salt = BlakeTwo256::hash_of(&offset_i); let outcome = OutcomeReport::Scalar(*outcome_index); - let secret = BlakeTwo256::hash_of(&(juror.clone(), outcome.clone(), salt)); + let secret = BlakeTwo256::hash_of(&(juror, outcome.clone(), salt)); draws .try_push(Draw { juror, @@ -689,14 +715,14 @@ fn get_winner_works() { prepare_draws(&market_id, outcomes_and_weights); let draws = >::get(market_id); - let winner = Court::get_winner(&draws.as_slice(), None).unwrap(); + let winner = Court::get_winner(draws.as_slice(), None).unwrap(); assert_eq!(winner, OutcomeReport::Scalar(1002u128)); let outcomes_and_weights = vec![(1000u128, 2), (1000u128, 4), (1001u128, 4), (1001u128, 3)]; prepare_draws(&market_id, outcomes_and_weights); let draws = >::get(market_id); - let winner = Court::get_winner(&draws.as_slice(), None).unwrap(); + let winner = Court::get_winner(draws.as_slice(), None).unwrap(); assert_eq!(winner, OutcomeReport::Scalar(1001u128)); }); } @@ -706,7 +732,7 @@ fn get_winner_returns_none_for_no_revealed_draws() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); let draws = >::get(market_id); - let winner = Court::get_winner(&draws.as_slice(), None); + let winner = Court::get_winner(draws.as_slice(), None); assert_eq!(winner, None); }); } @@ -715,7 +741,7 @@ fn get_winner_returns_none_for_no_revealed_draws() { fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); - let mut court = >::get(&market_id).unwrap(); + let mut court = >::get(market_id).unwrap(); // create a tie of two best outcomes let weights = vec![(1000u128, 42), (1001u128, 42)]; let appealed_outcome = OutcomeReport::Scalar(weights.len() as u128); @@ -728,7 +754,7 @@ fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { appealed_outcome: appealed_outcome.clone(), }) .unwrap(); - >::insert(&market_id, court); + >::insert(market_id, court); let latest = Court::get_latest_resolved_outcome(&market_id).unwrap(); assert_eq!(latest, appealed_outcome); From 869dbf6450eda60417dbad17e2bb46d043c3ef28 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 22 Mar 2023 11:48:45 +0100 Subject: [PATCH 143/368] modify params and add tests --- Cargo.lock | 1 + runtime/battery-station/src/parameters.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 4 +- zrml/court/Cargo.toml | 2 + zrml/court/src/lib.rs | 10 +- zrml/court/src/tests.rs | 432 +++++++++++++++++++++- 6 files changed, 441 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 868480313..d3a398b0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12922,6 +12922,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", + "test-case", "zeitgeist-primitives", "zrml-market-commons", ] diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 10ff26cf7..a2a61e989 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -79,7 +79,7 @@ parameter_types! { /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` /// was deposited. pub const AppealBond: Balance = 5 * BASE; - /// `OutcomeBond` is increased by this factor after every new appeal. + /// `AppealBond` is increased by this factor after every new appeal. pub const AppealBondFactor: Balance = 2 * BASE; /// Pallet identifier, mainly used for named balance reserves. pub const CourtPalletId: PalletId = COURT_PALLET_ID; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 67dcda0f5..4a95f8a66 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -79,8 +79,8 @@ parameter_types! { /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` /// was deposited. pub const AppealBond: Balance = 2000 * BASE; - /// `OutcomeBond` is increased by this factor after every new appeal. - pub const AppealBondFactor: Balance = 2 * BASE; + /// `AppealBond` is increased by this factor after every new appeal. + pub const AppealBondFactor: Balance = 200 * BASE; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. pub const CourtPalletId: PalletId = COURT_PALLET_ID; /// The time in which the jurors can cast their secret vote. diff --git a/zrml/court/Cargo.toml b/zrml/court/Cargo.toml index c7aeffca8..7b70d988a 100644 --- a/zrml/court/Cargo.toml +++ b/zrml/court/Cargo.toml @@ -18,6 +18,8 @@ pallet-timestamp = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github sp-io = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } zeitgeist-primitives = { default-features = false, features = ["mock"], path = "../../primitives" } +test-case = "2.0.2" + [features] default = ["std"] runtime-benchmarks = [ diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index f4bc5573f..0f1b9befb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -299,8 +299,8 @@ mod pallet { NotInAppealPeriod, /// The court is already present for this market. CourtAlreadyExists, - /// The caller of this extrinsic must be a randomly selected juror. - OnlyDrawnJurorsCanVote, + /// The caller of this extrinsic needs to be drawn or in the secret vote state. + InvalidVoteState, /// The amount is below the minimum required stake. BelowMinJurorStake, /// The maximum number of possible jurors has been reached. @@ -346,6 +346,8 @@ mod pallet { AmountBelowLowestJuror, /// This should not happen, because the juror account should only be once in a pool. JurorTwiceInPool, + /// The caller of this function is not part of the juror draws. + CallerNotInDraws, } #[pallet::hooks] @@ -563,11 +565,11 @@ mod pallet { // allow to override last vote ensure!( matches!(draws[index].vote, Vote::Drawn | Vote::Secret { secret: _ }), - Error::::OnlyDrawnJurorsCanVote + Error::::InvalidVoteState ); (index, draws[index].clone()) } - None => return Err(Error::::OnlyDrawnJurorsCanVote.into()), + None => return Err(Error::::CallerNotInDraws.into()), }; let vote = Vote::Secret { secret: secret_vote }; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 390d8d85b..666a4fc3b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -30,12 +30,13 @@ use crate::{ use frame_support::{assert_noop, assert_ok, traits::fungible::Balanced}; use pallet_balances::BalanceLock; use rand::seq::SliceRandom; -use sp_runtime::traits::{BlakeTwo256, Hash}; +use sp_runtime::traits::{BlakeTwo256, Hash, Zero}; +use test_case::test_case; use zeitgeist_primitives::{ constants::{ mock::{ - CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, MaxAppeals, - MaxJurors, MinJurorStake, RequestInterval, + CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, + DenounceSlashPercentage, MaxAppeals, MaxJurors, MinJurorStake, RequestInterval, }, BASE, }, @@ -118,6 +119,38 @@ fn prepare_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) Courts::::insert(market_id, court); } +fn put_alice_in_draw(market_id: crate::MarketIdOf) { + // trick a little bit to let alice be part of the ("random") selection + let mut draws = >::get(market_id); + assert!(!draws.is_empty()); + draws[0] = Draw { juror: ALICE, weight: 101, vote: Vote::Drawn, slashable: 42u128 }; + >::insert(market_id, draws); +} + +fn set_alice_after_vote( + outcome: OutcomeReport, +) -> ( + crate::MarketIdOf, + ::Hash, + ::Hash, +) { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + put_alice_in_draw(market_id); + + run_to_block(>::get() + 1); + + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); + + (market_id, secret, salt) +} + const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ JurorPoolItem { stake: 9, juror: HARRY, consumed_stake: 0 }, JurorPoolItem { stake: 8, juror: IAN, consumed_stake: 0 }, @@ -483,6 +516,399 @@ fn exit_court_fails_juror_not_prepared_to_exit() { }); } +#[test] +fn vote_works() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + put_alice_in_draw(market_id); + let old_draws = >::get(market_id); + + run_to_block(>::get() + 1); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); + System::assert_last_event(Event::JurorVoted { juror: ALICE, market_id, secret }.into()); + + let new_draws = >::get(market_id); + assert_eq!(old_draws[1..], new_draws[1..]); + assert_eq!(old_draws[0].juror, new_draws[0].juror); + assert_eq!(old_draws[0].weight, new_draws[0].weight); + assert_eq!(old_draws[0].slashable, new_draws[0].slashable); + assert_eq!(old_draws[0].vote, Vote::Drawn); + assert_eq!(new_draws[0].vote, Vote::Secret { secret }); + }); +} + +#[test] +fn vote_overwrite_works() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + put_alice_in_draw(market_id); + + run_to_block(>::get() + 1); + + let wrong_outcome = OutcomeReport::Scalar(69u128); + let salt = ::Hash::default(); + let wrong_secret = BlakeTwo256::hash_of(&(ALICE, wrong_outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, wrong_secret)); + + run_blocks(1); + + let right_outcome = OutcomeReport::Scalar(42u128); + let new_secret = BlakeTwo256::hash_of(&(ALICE, right_outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, new_secret)); + }); +} + +#[test] +fn vote_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0; + let secret = ::Hash::default(); + assert_noop!( + Court::vote(Origin::signed(ALICE), market_id, secret), + Error::::CourtNotFound + ); + }); +} + +#[test_case( + Vote::Revealed { + secret: ::Hash::default(), + outcome: OutcomeReport::Scalar(1u128), + salt: ::Hash::default(), + }; "revealed" +)] +#[test_case( + Vote::Denounced { + secret: ::Hash::default(), + outcome: OutcomeReport::Scalar(1u128), + salt: ::Hash::default(), + }; "denounced" +)] +fn vote_fails_if_vote_state_incorrect(vote: crate::Vote<::Hash>) { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + let mut draws = >::get(market_id); + assert!(!draws.is_empty()); + draws[0] = Draw { juror: ALICE, weight: 101, vote, slashable: 42u128 }; + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + assert_noop!( + Court::vote(Origin::signed(ALICE), market_id, secret), + Error::::InvalidVoteState + ); + }); +} + +#[test] +fn vote_fails_if_caller_not_in_draws() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let mut draws = >::get(market_id); + draws.retain(|draw| draw.juror != ALICE); + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + assert_noop!( + Court::vote(Origin::signed(ALICE), market_id, secret), + Error::::CallerNotInDraws + ); + }); +} + +#[test] +fn vote_fails_if_not_in_voting_period() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + put_alice_in_draw(market_id); + + run_to_block(>::get() + CourtVotePeriod::get() + 1); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + assert_noop!( + Court::vote(Origin::signed(ALICE), market_id, secret), + Error::::NotInVotingPeriod + ); + }); +} + +#[test] +fn reveal_vote_works() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + put_alice_in_draw(market_id); + + run_to_block(>::get() + 1); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); + + let old_draws = >::get(market_id); + + run_blocks(CourtVotePeriod::get() + 1); + + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt,)); + System::assert_last_event( + Event::JurorRevealedVote { juror: ALICE, market_id, outcome: outcome.clone(), salt } + .into(), + ); + + let new_draws = >::get(market_id); + assert_eq!(old_draws[1..], new_draws[1..]); + assert_eq!(old_draws[0].juror, new_draws[0].juror); + assert_eq!(old_draws[0].weight, new_draws[0].weight); + assert_eq!(old_draws[0].slashable, new_draws[0].slashable); + assert_eq!(old_draws[0].vote, Vote::Secret { secret }); + assert_eq!(new_draws[0].vote, Vote::Revealed { secret, outcome, salt }); + }); +} + +#[test] +fn reveal_vote_fails_if_caller_not_juror() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + 1); + + >::remove(ALICE); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::OnlyJurorsCanReveal + ); + }); +} + +#[test] +fn reveal_vote_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + run_blocks(CourtVotePeriod::get() + 1); + + >::remove(market_id); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::CourtNotFound + ); + }); +} + +#[test] +fn reveal_vote_fails_if_not_in_aggregation_period() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::NotInAggregationPeriod + ); + }); +} + +#[test] +fn reveal_vote_fails_if_juror_not_drawn() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + 1); + + >::mutate(market_id, |draws| { + draws.retain(|draw| draw.juror != ALICE); + }); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::JurorNotDrawn + ); + }); +} + +#[test] +fn reveal_vote_fails_for_invalid_reveal() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + 1); + + let invalid_outcome = OutcomeReport::Scalar(43u128); + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, invalid_outcome, salt), + Error::::InvalidReveal + ); + }); +} + +#[test] +fn reveal_vote_fails_if_juror_not_voted() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + 1); + + >::mutate(market_id, |draws| { + draws.iter_mut().for_each(|draw| { + if draw.juror == ALICE { + draw.vote = Vote::Drawn; + } + }); + }); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::JurorNotVoted + ); + }); +} + +#[test] +fn reveal_vote_fails_if_already_revealed() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + 1); + + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt)); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::VoteAlreadyRevealed + ); + }); +} + +#[test] +fn reveal_vote_fails_if_already_denounced() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + assert_ok!(Court::denounce_vote( + Origin::signed(BOB), + market_id, + ALICE, + outcome.clone(), + salt + )); + + run_blocks(CourtVotePeriod::get() + 1); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::VoteAlreadyDenounced + ); + }); +} + +#[test] +fn denounce_vote_works() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, secret, salt) = set_alice_after_vote(outcome.clone()); + + let old_draws = >::get(market_id); + assert!( + old_draws + .iter() + .any(|draw| { draw.juror == ALICE && matches!(draw.vote, Vote::Secret { .. }) }) + ); + + let free_alice_before = Balances::free_balance(ALICE); + let pot_balance_before = Balances::free_balance(&Court::reward_pot(&market_id)); + + assert_ok!(Court::denounce_vote( + Origin::signed(BOB), + market_id, + ALICE, + outcome.clone(), + salt + )); + System::assert_last_event( + Event::DenouncedJurorVote { + denouncer: BOB, + juror: ALICE, + market_id, + outcome: outcome.clone(), + salt, + } + .into(), + ); + + let new_draws = >::get(market_id); + assert_eq!(old_draws[1..], new_draws[1..]); + assert_eq!(old_draws[0].juror, ALICE); + assert_eq!(old_draws[0].juror, new_draws[0].juror); + assert_eq!(old_draws[0].weight, new_draws[0].weight); + assert_eq!(old_draws[0].slashable, new_draws[0].slashable); + assert_eq!(old_draws[0].vote, Vote::Secret { secret }); + assert_eq!(new_draws[0].vote, Vote::Denounced { secret, outcome, salt }); + + >::get().iter().for_each(|pool_item| { + assert_ne!(pool_item.juror, ALICE); + }); + + let free_alice_after = Balances::free_balance(ALICE); + let slash = DenounceSlashPercentage::get() * old_draws[0].slashable; + assert!(!slash.is_zero()); + assert_eq!(free_alice_after, free_alice_before - slash); + + let pot_balance_after = Balances::free_balance(&Court::reward_pot(&market_id)); + assert_eq!(pot_balance_after, pot_balance_before + slash); + }); +} + #[test] fn on_dispute_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { From 92fe6957f0dcf38a6924efde20ac0d15e48020b9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 22 Mar 2023 12:34:48 +0100 Subject: [PATCH 144/368] add denounce tests --- zrml/court/src/tests.rs | 170 ++++++++++++++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 22 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 666a4fc3b..a157e6e40 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -910,48 +910,174 @@ fn denounce_vote_works() { } #[test] -fn on_dispute_denies_non_court_markets() { +fn denounce_vote_fails_if_self_denounce() { ExtBuilder::default().build().execute_with(|| { - let mut market = DEFAULT_MARKET; - market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + assert_noop!( - Court::on_dispute(&0, &market), - Error::::MarketDoesNotHaveCourtMechanism + Court::denounce_vote(Origin::signed(ALICE), market_id, ALICE, outcome, salt), + Error::::SelfDenounceDisallowed ); }); } #[test] -fn get_resolution_outcome_denies_non_court_markets() { +fn denounce_vote_fails_if_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { - let mut market = DEFAULT_MARKET; - market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + >::remove(ALICE); + assert_noop!( - Court::get_resolution_outcome(&0, &market), - Error::::MarketDoesNotHaveCourtMechanism + Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Error::::JurorDoesNotExist ); }); } #[test] -fn appeal_stores_jurors_that_should_vote() { +fn denounce_vote_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { - run_to_block(123); + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + >::remove(market_id); + + assert_noop!( + Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Error::::CourtNotFound + ); + }); +} + +#[test] +fn denounce_vote_fails_if_not_in_voting_period() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + 1); + + assert_noop!( + Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Error::::NotInVotingPeriod + ); + }); +} + +#[test] +fn denounce_vote_fails_if_juror_not_drawn() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + >::mutate(market_id, |draws| { + draws.retain(|draw| draw.juror != ALICE); + }); + + assert_noop!( + Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Error::::JurorNotDrawn + ); + }); +} + +#[test] +fn denounce_vote_fails_if_invalid_reveal() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + let invalid_outcome = OutcomeReport::Scalar(69u128); + assert_noop!( + Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, invalid_outcome, salt), + Error::::InvalidReveal + ); + }); +} + +#[test] +fn denounce_vote_fails_if_juror_not_voted() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + >::mutate(market_id, |draws| { + draws.iter_mut().for_each(|draw| { + if draw.juror == ALICE { + draw.vote = Vote::Drawn; + } + }); + }); + + assert_noop!( + Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Error::::JurorNotVoted + ); + }); +} + +#[test] +fn denounce_vote_fails_if_vote_already_revealed() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + 1); + + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt)); + + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Error::::VoteAlreadyRevealed + ); + }); +} + +#[test] +fn denounce_vote_fails_if_vote_already_denounced() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + + assert_ok!(Court::denounce_vote( + Origin::signed(BOB), + market_id, + ALICE, + outcome.clone(), + salt + )); + + assert_noop!( + Court::denounce_vote(Origin::signed(CHARLIE), market_id, ALICE, outcome, salt), + Error::::VoteAlreadyDenounced + ); }); } -// Alice is the winner, Bob is tardy and Charlie is the loser #[test] -fn get_resolution_outcome_awards_winners_and_slashes_losers() { +fn on_dispute_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { - run_to_block(2); - let amount_alice = 2 * BASE; - let amount_bob = 3 * BASE; - let amount_charlie = 4 * BASE; - Court::join_court(Origin::signed(ALICE), amount_alice).unwrap(); - Court::join_court(Origin::signed(BOB), amount_bob).unwrap(); - Court::join_court(Origin::signed(CHARLIE), amount_charlie).unwrap(); - MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let mut market = DEFAULT_MARKET; + market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + assert_noop!( + Court::on_dispute(&0, &market), + Error::::MarketDoesNotHaveCourtMechanism + ); + }); +} + +#[test] +fn get_resolution_outcome_denies_non_court_markets() { + ExtBuilder::default().build().execute_with(|| { + let mut market = DEFAULT_MARKET; + market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + assert_noop!( + Court::get_resolution_outcome(&0, &market), + Error::::MarketDoesNotHaveCourtMechanism + ); }); } From 37cb195be2eddf2c5ef33af34fa13e2c50edff07 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 22 Mar 2023 16:19:14 +0100 Subject: [PATCH 145/368] increase code safety --- zrml/court/src/lib.rs | 53 +++++++++++++------------- zrml/court/src/tests.rs | 83 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 33 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 0f1b9befb..4dc433abf 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -514,16 +514,18 @@ mod pallet { Jurors::::remove(&juror); (prev_juror_info.stake, >::zero()) } else { + let active_lock = prev_juror_info.active_lock; + let exit_amount = prev_juror_info.stake.saturating_sub(active_lock); T::Currency::set_lock( T::CourtLockId::get(), &juror, - prev_juror_info.active_lock, + active_lock, WithdrawReasons::all(), ); - let active_lock = prev_juror_info.active_lock; - let exit_amount = prev_juror_info.stake.saturating_sub(active_lock); + prev_juror_info.stake = active_lock; Jurors::::insert(&juror, prev_juror_info); + (exit_amount, active_lock) }; @@ -764,15 +766,18 @@ mod pallet { ); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; + // the outcome which would be resolved on is appealed (including oracle report) - let appealed_outcome = Self::get_latest_resolved_outcome(&market_id)?; + let old_draws = Draws::::get(market_id); + let appealed_outcome = + Self::get_latest_resolved_outcome(&market_id, old_draws.as_slice())?; let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; court.appeals.try_push(appeal_info).map_err(|_| { debug_assert!(false, "Appeal bound is checked above."); Error::::MaxAppealsReached })?; - Self::select_jurors(&market_id, appeal_number)?; + let new_draws = Self::select_jurors(&market_id, old_draws, appeal_number)?; let last_resolve_at = court.periods.appeal_end; let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); @@ -796,6 +801,7 @@ mod pallet { T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; >::insert(market_id, court); + >::insert(market_id, new_draws); let appeal_number = appeal_number as u32; Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); @@ -829,7 +835,9 @@ mod pallet { Self::check_appealable_market(&market_id, &court, now)?; // the outcome which would be resolved on is appealed (including oracle report) - let appealed_outcome = Self::get_latest_resolved_outcome(&market_id)?; + let old_draws = Draws::::get(market_id); + let appealed_outcome = + Self::get_latest_resolved_outcome(&market_id, old_draws.as_slice())?; let bond = default_appeal_bond::(appeal_number); let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; @@ -1086,8 +1094,9 @@ mod pallet { pub(crate) fn select_jurors( market_id: &MarketIdOf, + last_draws: DrawsOf, appeal_number: usize, - ) -> Result<(), DispatchError> { + ) -> Result, DispatchError> { let mut jurors: JurorPoolOf = JurorPool::::get(); let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); ensure!(jurors.len() >= necessary_jurors_weight, Error::::NotEnoughJurors); @@ -1103,7 +1112,7 @@ mod pallet { `MaxDraws`." ); // keep in mind that the old draw likely contains different jurors - for old_draw in >::get(market_id) { + for old_draw in &last_draws { if let Some(mut juror_info) = >::get(&old_draw.juror) { juror_info.active_lock = juror_info.active_lock.saturating_sub(old_draw.slashable); @@ -1119,23 +1128,11 @@ mod pallet { } } let new_draws = >::truncate_from(random_jurors); - debug_assert!( - if appeal_number > 0 { - appeal_number == >::get(market_id).unwrap().appeals.len() - } else { - // for the first court round, we have no previous winner - true - }, - "Before the draws are overwritten, we need to ensure that the winner of the last \ - appeal round was determined with 'get_latest_resolved_outcome' (the data of the \ - last Draws)." - ); // new appeal round should have a fresh set of draws - >::insert(market_id, new_draws); // modified consumed_stake for each selected juror >::put(jurors); - Ok(()) + Ok(new_draws) } // Returns (index, pool_item) if the stake associated with the juror was found. @@ -1315,6 +1312,7 @@ mod pallet { pub(crate) fn get_latest_resolved_outcome( market_id: &MarketIdOf, + last_draws: &[DrawOf], ) -> Result { let market = T::MarketCommons::market(market_id)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -1325,9 +1323,8 @@ mod pallet { .unwrap_or(None); let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; let oracle_outcome = report.outcome.clone(); - let draws = Draws::::get(market_id); let resolved_outcome = - Self::get_winner(draws.as_slice(), last_winner).unwrap_or(oracle_outcome); + Self::get_winner(last_draws, last_winner).unwrap_or(oracle_outcome); Ok(resolved_outcome) } } @@ -1353,7 +1350,11 @@ mod pallet { ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); let appeal_number = 0usize; - Self::select_jurors(market_id, appeal_number)?; + let new_draws = Self::select_jurors( + market_id, + >::truncate_from(Vec::new()), + appeal_number, + )?; let now = >::block_number(); let request_block = >::get(); @@ -1373,6 +1374,7 @@ mod pallet { let _ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.periods.appeal_end)?; + >::insert(market_id, new_draws); >::insert(market_id, court); Ok(()) @@ -1388,7 +1390,8 @@ mod pallet { ); let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let resolved_outcome = Self::get_latest_resolved_outcome(market_id)?; + let draws = Draws::::get(market_id); + let resolved_outcome = Self::get_latest_resolved_outcome(market_id, draws.as_slice())?; court.status = CourtStatus::Closed { winner: resolved_outcome.clone(), punished: false, diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a157e6e40..5d9990391 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -123,7 +123,7 @@ fn put_alice_in_draw(market_id: crate::MarketIdOf) { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(market_id); assert!(!draws.is_empty()); - draws[0] = Draw { juror: ALICE, weight: 101, vote: Vote::Drawn, slashable: 42u128 }; + draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: 42u128 }; >::insert(market_id, draws); } @@ -988,7 +988,7 @@ fn denounce_vote_fails_if_juror_not_drawn() { fn denounce_vote_fails_if_invalid_reveal() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (market_id, _, salt) = set_alice_after_vote(outcome); let invalid_outcome = OutcomeReport::Scalar(69u128); assert_noop!( @@ -1057,6 +1057,68 @@ fn denounce_vote_fails_if_vote_already_denounced() { }); } +#[test] +fn appeal_overrides_last_draws() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + let neccessary_juror_weight = Court::necessary_jurors_weight(1usize) as u32; + let draws = >::get(market_id); + let draws_total_weight = draws.iter().map(|draw| draw.weight).sum::(); + assert_eq!(draws_total_weight, neccessary_juror_weight); + }); +} + +#[test] +fn appeal_selects_jurors_after_appealed_outcome_fetch() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + let last_appealed_outcome = >::get(market_id) + .unwrap() + .appeals + .last() + .unwrap() + .appealed_outcome + .clone(); + + let request_block = >::get(); + run_to_block(request_block + 1); + let outcome = OutcomeReport::Scalar(69u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); + + run_blocks(CourtVotePeriod::get() + 1); + + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt)); + + run_blocks(CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + let new_appealed_outcome = >::get(market_id) + .unwrap() + .appeals + .last() + .unwrap() + .appealed_outcome + .clone(); + + assert_ne!(last_appealed_outcome, new_appealed_outcome); + }); +} + #[test] fn on_dispute_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { @@ -1115,10 +1177,10 @@ fn select_jurors_updates_juror_consumed_stake() { let jurors = JurorPool::::get(); let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); - Court::select_jurors(&market_id, appeal_number).unwrap(); + let old_draws = >::get(market_id); + let new_draws = Court::select_jurors(&market_id, old_draws, appeal_number).unwrap(); - let draws = >::get(market_id); - let total_draw_slashable = draws.iter().map(|draw| draw.slashable).sum::(); + let total_draw_slashable = new_draws.iter().map(|draw| draw.slashable).sum::(); let jurors = JurorPool::::get(); let consumed_stake_after = jurors.iter().map(|juror| juror.consumed_stake).sum::(); assert_ne!(consumed_stake_before, consumed_stake_after); @@ -1143,7 +1205,7 @@ fn select_jurors_reduces_active_lock_for_old_draw() { assert_eq!(juror_info.active_lock, draw.slashable); }); - Court::select_jurors(&market_id, appeal_number).unwrap(); + Court::select_jurors(&market_id, old_draws.clone(), appeal_number).unwrap(); old_draws.iter().for_each(|draw| { let juror = draw.juror; @@ -1308,7 +1370,8 @@ fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { .unwrap(); >::insert(market_id, court); - let latest = Court::get_latest_resolved_outcome(&market_id).unwrap(); + let draws = >::get(market_id); + let latest = Court::get_latest_resolved_outcome(&market_id, draws.as_slice()).unwrap(); assert_eq!(latest, appealed_outcome); assert!(latest != ORACLE_REPORT); }); @@ -1320,7 +1383,11 @@ fn get_latest_resolved_outcome_selects_oracle_report() { let market_id = initialize_court(); let market = MarketCommons::market(&market_id).unwrap(); assert_eq!(market.report.unwrap().outcome, ORACLE_REPORT); - assert_eq!(Court::get_latest_resolved_outcome(&market_id).unwrap(), ORACLE_REPORT); + let draws = >::get(market_id); + assert_eq!( + Court::get_latest_resolved_outcome(&market_id, draws.as_slice()).unwrap(), + ORACLE_REPORT + ); }); } From 4d9afab22594a720ce3fac8ea03cfd8dcfdce5f3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 24 Mar 2023 12:35:48 +0100 Subject: [PATCH 146/368] add tests --- zrml/court/src/lib.rs | 3 + zrml/court/src/mock.rs | 40 ++-- zrml/court/src/mock_storage.rs | 52 +++++ zrml/court/src/tests.rs | 348 +++++++++++++++++++++++++++++++-- 4 files changed, 410 insertions(+), 33 deletions(-) create mode 100644 zrml/court/src/mock_storage.rs diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 4dc433abf..6a06ca206 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -29,6 +29,7 @@ mod benchmarks; mod court_pallet_api; pub mod migrations; mod mock; +mod mock_storage; mod tests; mod types; pub mod weights; @@ -852,6 +853,8 @@ mod pallet { let last_resolve_at = court.periods.appeal_end; let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + >::insert(market_id, court); + Self::deposit_event(Event::GlobalDisputeBacked { market_id }); Ok(()) diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 3fb1cfbdb..ed8e98867 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -17,7 +17,7 @@ #![cfg(test)] -use crate::{self as zrml_court}; +use crate::{self as zrml_court, mock_storage::pallet as mock_storage}; use frame_support::{ construct_runtime, pallet_prelude::{DispatchError, Weight}, @@ -72,13 +72,19 @@ construct_runtime!( RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, System: frame_system::{Call, Config, Event, Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet}, + // Just a mock storage for testing. + MockStorage: mock_storage::{Storage}, } ); -// NoopResolution implements DisputeResolutionApi with no-ops. -pub struct NoopResolution; +// MockResolution implements DisputeResolutionApi with no-ops. +pub struct MockResolution; -impl DisputeResolutionApi for NoopResolution { +impl mock_storage::Config for Runtime { + type MarketCommons = MarketCommons; +} + +impl DisputeResolutionApi for MockResolution { type AccountId = AccountIdTest; type Balance = Balance; type BlockNumber = BlockNumber; @@ -99,18 +105,28 @@ impl DisputeResolutionApi for NoopResolution { } fn add_auto_resolve( - _market_id: &Self::MarketId, - _resolve_at: Self::BlockNumber, + market_id: &Self::MarketId, + resolve_at: Self::BlockNumber, ) -> Result { - Ok(0u32) + let ids_len = >::try_mutate( + resolve_at, + |ids| -> Result { + ids.try_push(*market_id).map_err(|_| DispatchError::Other("Storage Overflow"))?; + Ok(ids.len() as u32) + }, + )?; + Ok(ids_len) } - fn auto_resolve_exists(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> bool { - false + fn auto_resolve_exists(market_id: &Self::MarketId, resolve_at: Self::BlockNumber) -> bool { + >::get(resolve_at).contains(market_id) } - fn remove_auto_resolve(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> u32 { - 0u32 + fn remove_auto_resolve(market_id: &Self::MarketId, resolve_at: Self::BlockNumber) -> u32 { + >::mutate(resolve_at, |ids| -> u32 { + ids.retain(|id| id != market_id); + ids.len() as u32 + }) } } @@ -123,7 +139,7 @@ impl crate::Config for Runtime { type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; type DenounceSlashPercentage = DenounceSlashPercentage; - type DisputeResolution = NoopResolution; + type DisputeResolution = MockResolution; type Event = Event; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; diff --git a/zrml/court/src/mock_storage.rs b/zrml/court/src/mock_storage.rs new file mode 100644 index 000000000..c7908fa55 --- /dev/null +++ b/zrml/court/src/mock_storage.rs @@ -0,0 +1,52 @@ +// Copyright 2022 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +#![cfg(test)] +#![allow(dead_code)] + +#[frame_support::pallet] +pub(crate) mod pallet { + use core::marker::PhantomData; + use frame_support::pallet_prelude::*; + use zrml_market_commons::MarketCommonsPalletApi; + + pub(crate) type MarketIdOf = + <::MarketCommons as MarketCommonsPalletApi>::MarketId; + + pub type CacheSize = ConstU32<64>; + + #[pallet::config] + pub trait Config: frame_system::Config { + type MarketCommons: MarketCommonsPalletApi< + AccountId = Self::AccountId, + BlockNumber = Self::BlockNumber, + >; + } + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + /// Only used for testing the dispute resolution API to prediction-markets + #[pallet::storage] + pub(crate) type MarketIdsPerDisputeBlock = StorageMap< + _, + Twox64Concat, + T::BlockNumber, + BoundedVec, CacheSize>, + ValueQuery, + >; +} diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 5d9990391..96e1e5fac 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -23,6 +23,7 @@ use crate::{ System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, POOR_PAUL, }, + mock_storage::pallet::MarketIdsPerDisputeBlock, types::{CourtStatus, Draw, Vote}, AccountIdLookupOf, AppealInfo, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, JurorPoolOf, Jurors, MarketOf, RequestBlock, @@ -47,7 +48,7 @@ use zeitgeist_primitives::{ ScoringRule, }, }; -use zrml_market_commons::MarketCommonsPalletApi; +use zrml_market_commons::{Error as MError, MarketCommonsPalletApi}; const ORACLE_REPORT: OutcomeReport = OutcomeReport::Scalar(u128::MAX); @@ -101,7 +102,7 @@ fn fill_juror_pool() { } } -fn prepare_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { +fn fill_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { let mut court = Courts::::get(market_id).unwrap(); let mut number = 0u128; while (number as usize) < appeal_number { @@ -1057,12 +1058,125 @@ fn denounce_vote_fails_if_vote_already_denounced() { }); } +#[test] +fn appeal_updates_periods() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + let old_request_block = >::get(); + let last_court = >::get(market_id).unwrap(); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + let now = >::block_number(); + let court = >::get(market_id).unwrap(); + + let new_request_block = >::get(); + assert_eq!(old_request_block + RequestInterval::get(), new_request_block); + + let pre_vote_end = new_request_block - now; + assert_eq!(court.periods.pre_vote_end, now + pre_vote_end); + assert_eq!(court.periods.vote_end, court.periods.pre_vote_end + CourtVotePeriod::get()); + assert_eq!( + court.periods.aggregation_end, + court.periods.vote_end + CourtAggregationPeriod::get() + ); + assert_eq!( + court.periods.appeal_end, + court.periods.aggregation_end + CourtAppealPeriod::get() + ); + + assert!(last_court.periods.pre_vote_end < court.periods.pre_vote_end); + assert!(last_court.periods.vote_end < court.periods.vote_end); + assert!(last_court.periods.aggregation_end < court.periods.aggregation_end); + assert!(last_court.periods.appeal_end < court.periods.appeal_end); + }); +} + +#[test] +fn appeal_reserves_default_appeal_bond() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let free_charlie_before = Balances::free_balance(CHARLIE); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + let free_charlie_after = Balances::free_balance(CHARLIE); + let bond = crate::default_appeal_bond::(1usize); + assert!(!bond.is_zero()); + assert_eq!(free_charlie_after, free_charlie_before - bond); + assert_eq!(Balances::reserved_balance(CHARLIE), bond); + }); +} + +#[test] +fn appeal_emits_event() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + System::assert_last_event(Event::MarketAppealed { market_id, appeal_number: 1u32 }.into()); + }); +} + +#[test] +fn appeal_shifts_auto_resolve() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + let resolve_at_0 = >::get(market_id).unwrap().periods.appeal_end; + assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![0]); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + let resolve_at_1 = >::get(market_id).unwrap().periods.appeal_end; + assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_1), vec![0]); + assert_ne!(resolve_at_0, resolve_at_1); + assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![]); + }); +} + #[test] fn appeal_overrides_last_draws() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); + let last_draws = >::get(market_id); + assert!(!last_draws.len().is_zero()); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + let draws = >::get(market_id); + assert_ne!(draws, last_draws); + }); +} + +#[test] +fn appeal_draws_total_weight_is_correct() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + let last_draws = >::get(market_id); + let last_draws_total_weight = last_draws.iter().map(|draw| draw.weight).sum::(); + assert_eq!(last_draws_total_weight, Court::necessary_jurors_weight(0usize) as u32); + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); @@ -1075,7 +1189,7 @@ fn appeal_overrides_last_draws() { } #[test] -fn appeal_selects_jurors_after_appealed_outcome_fetch() { +fn appeal_get_latest_resolved_outcome_changes() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); @@ -1101,7 +1215,7 @@ fn appeal_selects_jurors_after_appealed_outcome_fetch() { run_blocks(CourtVotePeriod::get() + 1); - assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt)); + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt)); run_blocks(CourtAggregationPeriod::get() + 1); @@ -1115,10 +1229,214 @@ fn appeal_selects_jurors_after_appealed_outcome_fetch() { .appealed_outcome .clone(); + // if the new appealed outcome were the last appealed outcome, + // then the wrong appealed outcome was added in `appeal` + assert_eq!(new_appealed_outcome, outcome); assert_ne!(last_appealed_outcome, new_appealed_outcome); }); } +#[test] +fn appeal_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!(Court::appeal(Origin::signed(CHARLIE), 0), Error::::CourtNotFound); + }); +} + +#[test] +fn appeal_fails_if_insufficient_amount() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_noop!( + Court::appeal(Origin::signed(POOR_PAUL), market_id), + Error::::InsufficientAmount + ); + }); +} + +#[test] +fn appeal_fails_if_only_global_dispute_appeal_allowed() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_noop!( + Court::appeal(Origin::signed(CHARLIE), market_id), + Error::::OnlyGlobalDisputeAppealAllowed + ); + }); +} + +#[test] +fn check_appealable_market_fails_if_market_not_found() { + ExtBuilder::default().build().execute_with(|| { + let now = >::block_number(); + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let court = >::get(market_id).unwrap(); + MarketCommons::remove_market(&market_id).unwrap(); + + assert_noop!( + Court::check_appealable_market(&0, &court, now), + MError::::MarketDoesNotExist + ); + }); +} + +#[test] +fn check_appealable_market_fails_if_dispute_mechanism_wrong() { + ExtBuilder::default().build().execute_with(|| { + let now = >::block_number(); + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let court = >::get(market_id).unwrap(); + + MarketCommons::mutate_market(&market_id, |market| { + market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + Ok(()) + }) + .unwrap(); + + assert_noop!( + Court::check_appealable_market(&0, &court, now), + Error::::MarketDoesNotHaveCourtMechanism + ); + }); +} + +#[test] +fn check_appealable_market_fails_if_not_in_appeal_period() { + ExtBuilder::default().build().execute_with(|| { + let now = >::block_number(); + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get()); + + let court = >::get(market_id).unwrap(); + + assert_noop!( + Court::check_appealable_market(&0, &court, now), + Error::::NotInAppealPeriod + ); + }); +} + +#[test] +fn back_global_dispute_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Court::back_global_dispute(Origin::signed(CHARLIE), 0), + Error::::CourtNotFound + ); + }); +} + +#[test] +fn back_global_dispute_fails_if_needs_to_be_last_appeal() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_noop!( + Court::back_global_dispute(Origin::signed(CHARLIE), market_id), + Error::::NeedsToBeLastAppeal + ); + }); +} + +#[test] +fn back_global_dispute_reserves_default_appeal_bond() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + + let default_appeal_bond = crate::default_appeal_bond::(MaxAppeals::get() as usize); + assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); + assert_eq!(Balances::reserved_balance(&CHARLIE), default_appeal_bond); + }); +} + +#[test] +fn back_global_dispute_emits_event() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + + assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); + + System::assert_last_event(Event::GlobalDisputeBacked { market_id }.into()); + }); +} + +#[test] +fn back_global_dispute_removes_auto_resolve() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + + let court = >::get(market_id).unwrap(); + let resolve_at = court.periods.appeal_end; + assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); + + assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); + + assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![]); + }); +} + +#[test] +fn back_global_dispute_adds_last_appeal() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + + let last_draws = >::get(market_id); + let appealed_outcome = + Court::get_latest_resolved_outcome(&market_id, last_draws.as_slice()).unwrap(); + + assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); + + let court = >::get(market_id).unwrap(); + assert!(court.appeals.is_full()); + + let last_appeal = court.appeals.last().unwrap(); + assert_eq!(last_appeal.appealed_outcome, appealed_outcome); + }); +} + #[test] fn on_dispute_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { @@ -1172,7 +1490,7 @@ fn select_jurors_updates_juror_consumed_stake() { fill_juror_pool(); // the last appeal is reserved for global dispute backing let appeal_number = (MaxAppeals::get() - 1) as usize; - prepare_appeals(&market_id, appeal_number); + fill_appeals(&market_id, appeal_number); let jurors = JurorPool::::get(); let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); @@ -1194,7 +1512,7 @@ fn select_jurors_reduces_active_lock_for_old_draw() { let market_id = initialize_court(); fill_juror_pool(); let appeal_number = 1usize; - prepare_appeals(&market_id, appeal_number); + fill_appeals(&market_id, appeal_number); let old_draws = >::get(market_id); assert!(!old_draws.is_empty()); @@ -1251,21 +1569,9 @@ fn has_failed_returns_true_for_appealable_court_appeals_full() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); let market = MarketCommons::market(&market_id).unwrap(); - let mut court = >::get(market_id).unwrap(); - let mut number = 0u128; - while !court.appeals.is_full() { - let appealed_outcome = OutcomeReport::Scalar(number); - court - .appeals - .try_push(AppealInfo { - backer: number, - bond: crate::default_appeal_bond::(court.appeals.len()), - appealed_outcome, - }) - .unwrap(); - number += 1; - } - >::insert(market_id, court); + + fill_appeals(&market_id, MaxAppeals::get() as usize); + assert!(Court::has_failed(&market_id, &market).unwrap()); }); } From d3f55ace6b81e2d23d73bb660f0c8fcd6d8e6a1e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 24 Mar 2023 16:35:29 +0100 Subject: [PATCH 147/368] add tests --- zrml/court/src/lib.rs | 6 +- zrml/court/src/tests.rs | 649 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 651 insertions(+), 4 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 6a06ca206..50dece6a6 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -323,8 +323,8 @@ mod pallet { SelfDenounceDisallowed, /// The court is not in the closed state. CourtNotClosed, - /// The jurors were already reassigned. - JurorsAlreadyReassigned, + /// The juror stakes were already reassigned. + JurorStakesAlreadyReassigned, /// The tardy jurors were already punished. TardyJurorsAlreadyPunished, /// Punish the tardy jurors first. @@ -959,7 +959,7 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { CourtStatus::Closed { winner, punished, reassigned } => { - ensure!(!reassigned, Error::::JurorsAlreadyReassigned); + ensure!(!reassigned, Error::::JurorStakesAlreadyReassigned); ensure!(punished, Error::::PunishTardyJurorsFirst); winner } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 96e1e5fac..d8cc1e4b3 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -37,7 +37,8 @@ use zeitgeist_primitives::{ constants::{ mock::{ CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, - DenounceSlashPercentage, MaxAppeals, MaxJurors, MinJurorStake, RequestInterval, + DenounceSlashPercentage, MaxAppeals, MaxJurors, MinJurorStake, + RedistributionPercentage, RequestInterval, TardySlashPercentage, }, BASE, }, @@ -1437,6 +1438,652 @@ fn back_global_dispute_adds_last_appeal() { }); } +#[test] +fn punish_tardy_jurors_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Court::punish_tardy_jurors(Origin::signed(CHARLIE), 0), + Error::::CourtNotFound + ); + }); +} + +#[test] +fn punish_tardy_jurors_fails_if_court_not_closed() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_noop!( + Court::punish_tardy_jurors(Origin::signed(CHARLIE), market_id), + Error::::CourtNotClosed + ); + }); +} + +#[test] +fn punish_tardy_jurors_fails_if_tardy_jurors_already_punished() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + assert_noop!( + Court::punish_tardy_jurors(Origin::signed(CHARLIE), market_id), + Error::::TardyJurorsAlreadyPunished + ); + }); +} + +#[test] +fn punish_tardy_jurors_emits_event() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + System::assert_last_event(Event::TardyJurorsPunished { market_id }.into()); + }); +} + +#[test] +fn punish_tardy_jurors_updates_court_status() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let court = >::get(market_id).unwrap(); + assert_eq!(court.status, CourtStatus::Open); + + let market = MarketCommons::market(&market_id).unwrap(); + let resolution_outcome = + Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + + let court = >::get(market_id).unwrap(); + assert_eq!( + court.status, + CourtStatus::Closed { + winner: resolution_outcome.clone(), + reassigned: false, + punished: false + } + ); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + let court = >::get(market_id).unwrap(); + assert_eq!( + court.status, + CourtStatus::Closed { winner: resolution_outcome, reassigned: false, punished: true } + ); + }); +} + +#[test] +fn punish_tardy_jurors_slashes_tardy_jurors_only() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::join_court(Origin::signed(BOB), amount)); + assert_ok!(Court::join_court(Origin::signed(CHARLIE), amount)); + assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); + assert_ok!(Court::join_court(Origin::signed(EVE), amount)); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + + let draws: crate::DrawsOf = vec![ + Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() }, + Draw { + juror: BOB, + weight: 1, + vote: Vote::Secret { secret }, + slashable: 2 * MinJurorStake::get(), + }, + Draw { + juror: CHARLIE, + weight: 1, + vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + slashable: 3 * MinJurorStake::get(), + }, + Draw { juror: DAVE, weight: 1, vote: Vote::Drawn, slashable: 4 * MinJurorStake::get() }, + Draw { + juror: EVE, + weight: 1, + vote: Vote::Denounced { secret, outcome, salt }, + slashable: 5 * MinJurorStake::get(), + }, + ] + .try_into() + .unwrap(); + let old_draws = draws.clone(); + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + run_blocks( + CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, + ); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + + let free_alice_before = Balances::free_balance(&ALICE); + let free_bob_before = Balances::free_balance(&BOB); + let free_charlie_before = Balances::free_balance(&CHARLIE); + let free_dave_before = Balances::free_balance(&DAVE); + let free_eve_before = Balances::free_balance(&EVE); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + let free_alice_after = Balances::free_balance(&ALICE); + assert_ne!(free_alice_after, free_alice_before); + assert_eq!( + free_alice_after, + free_alice_before - TardySlashPercentage::get() * old_draws[ALICE as usize].slashable + ); + + let free_bob_after = Balances::free_balance(&BOB); + assert_ne!(free_bob_after, free_bob_before); + assert_eq!( + free_bob_after, + free_bob_before - TardySlashPercentage::get() * old_draws[BOB as usize].slashable + ); + + let free_charlie_after = Balances::free_balance(&CHARLIE); + assert_eq!(free_charlie_after, free_charlie_before); + + let free_dave_after = Balances::free_balance(&DAVE); + assert_ne!(free_dave_after, free_dave_before); + assert_eq!( + free_dave_after, + free_dave_before - TardySlashPercentage::get() * old_draws[DAVE as usize].slashable + ); + + let free_eve_after = Balances::free_balance(&EVE); + assert_eq!(free_eve_after, free_eve_before); + }); +} + +#[test] +fn punish_tardy_jurors_removes_tardy_jurors_from_pool() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::join_court(Origin::signed(BOB), amount)); + assert_ok!(Court::join_court(Origin::signed(CHARLIE), amount)); + assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + + let draws: crate::DrawsOf = vec![ + Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() }, + Draw { + juror: BOB, + weight: 1, + vote: Vote::Secret { secret }, + slashable: 2 * MinJurorStake::get(), + }, + Draw { + juror: CHARLIE, + weight: 1, + vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + slashable: 3 * MinJurorStake::get(), + }, + Draw { + juror: DAVE, + weight: 1, + vote: Vote::Denounced { secret, outcome, salt }, + slashable: 4 * MinJurorStake::get(), + }, + ] + .try_into() + .unwrap(); + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + run_blocks( + CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, + ); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + let pool = >::get(); + assert!(!pool.clone().into_inner().iter().any(|pool_item| pool_item.juror == ALICE)); + assert!(!pool.clone().into_inner().iter().any(|pool_item| pool_item.juror == BOB)); + assert!(pool.clone().into_inner().iter().any(|pool_item| pool_item.juror == CHARLIE)); + assert!(pool.into_inner().iter().any(|pool_item| pool_item.juror == DAVE)); + }); +} + +#[test] +fn reassign_juror_stakes_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Court::reassign_juror_stakes(Origin::signed(EVE), 0), + Error::::CourtNotFound + ); + }); +} + +#[test] +fn reassign_juror_stakes_fails_if_tardy_jurors_not_punished() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + assert_noop!( + Court::reassign_juror_stakes(Origin::signed(EVE), market_id), + Error::::PunishTardyJurorsFirst + ); + }); +} + +#[test] +fn reassign_juror_stakes_emits_event() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + System::assert_last_event(Event::JurorStakesReassigned { market_id }.into()); + }); +} + +#[test] +fn reassign_juror_stakes_fails_if_juror_stakes_already_reassigned() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_noop!( + Court::reassign_juror_stakes(Origin::signed(EVE), market_id), + Error::::JurorStakesAlreadyReassigned + ); + }); +} + +#[test] +fn reassign_juror_stakes_updates_court_status() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let market = MarketCommons::market(&market_id).unwrap(); + let resolution_outcome = + Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + let court = >::get(market_id).unwrap(); + assert_eq!( + court.status, + CourtStatus::Closed { + winner: resolution_outcome.clone(), + punished: true, + reassigned: false + } + ); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + + let court = >::get(market_id).unwrap(); + assert_eq!( + court.status, + CourtStatus::Closed { winner: resolution_outcome, punished: true, reassigned: true } + ); + }); +} + +#[test] +fn reassign_juror_stakes_removes_draws() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + let draws = >::get(market_id); + assert!(!draws.is_empty()); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + + let draws = >::get(market_id); + assert!(draws.is_empty()); + }); +} + +#[test] +fn reassign_juror_stakes_fails_if_court_not_closed() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + assert_noop!( + Court::reassign_juror_stakes(Origin::signed(EVE), market_id), + Error::::CourtNotClosed + ); + }); +} + +#[test] +fn reassign_juror_stakes_decreases_active_lock() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::join_court(Origin::signed(BOB), amount)); + assert_ok!(Court::join_court(Origin::signed(CHARLIE), amount)); + assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + + let alice_slashable = MinJurorStake::get(); + >::mutate(ALICE, |juror_info| { + if let Some(ref mut info) = juror_info { + info.active_lock = alice_slashable; + } + }); + let bob_slashable = 2 * MinJurorStake::get(); + >::mutate(BOB, |juror_info| { + if let Some(ref mut juror_info) = juror_info { + juror_info.active_lock = bob_slashable; + } + }); + let charlie_slashable = 3 * MinJurorStake::get(); + >::mutate(CHARLIE, |juror_info| { + if let Some(ref mut juror_info) = juror_info { + juror_info.active_lock = charlie_slashable; + } + }); + let dave_slashable = 4 * MinJurorStake::get(); + >::mutate(DAVE, |juror_info| { + if let Some(ref mut juror_info) = juror_info { + juror_info.active_lock = dave_slashable; + } + }); + + let draws: crate::DrawsOf = vec![ + Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: alice_slashable }, + Draw { juror: BOB, weight: 1, vote: Vote::Secret { secret }, slashable: bob_slashable }, + Draw { + juror: CHARLIE, + weight: 1, + vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + slashable: charlie_slashable, + }, + Draw { + juror: DAVE, + weight: 1, + vote: Vote::Denounced { secret, outcome, salt }, + slashable: dave_slashable, + }, + ] + .try_into() + .unwrap(); + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + run_blocks( + CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, + ); + + let market = MarketCommons::market(&market_id).unwrap(); + let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert!(>::get(ALICE).unwrap().active_lock.is_zero()); + assert!(>::get(BOB).unwrap().active_lock.is_zero()); + assert!(>::get(CHARLIE).unwrap().active_lock.is_zero()); + assert!(>::get(DAVE).unwrap().active_lock.is_zero()); + }); +} + +#[test] +fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::join_court(Origin::signed(BOB), amount)); + assert_ok!(Court::join_court(Origin::signed(CHARLIE), amount)); + assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + + let wrong_outcome_0 = OutcomeReport::Scalar(69u128); + let wrong_outcome_1 = OutcomeReport::Scalar(56u128); + + let draws: crate::DrawsOf = vec![ + Draw { + juror: ALICE, + weight: 1, + vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + slashable: MinJurorStake::get(), + }, + Draw { + juror: BOB, + weight: 1, + vote: Vote::Revealed { secret, outcome: wrong_outcome_0, salt }, + slashable: 2 * MinJurorStake::get(), + }, + Draw { + juror: CHARLIE, + weight: 1, + vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + slashable: 3 * MinJurorStake::get(), + }, + Draw { + juror: DAVE, + weight: 1, + vote: Vote::Revealed { secret, outcome: wrong_outcome_1, salt }, + slashable: 4 * MinJurorStake::get(), + }, + ] + .try_into() + .unwrap(); + let last_draws = draws.clone(); + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + run_blocks( + CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, + ); + + let market = MarketCommons::market(&market_id).unwrap(); + let resolution_outcome = + Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + assert_eq!(resolution_outcome, outcome); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + let free_alice_before = Balances::free_balance(ALICE); + let free_bob_before = Balances::free_balance(BOB); + let free_charlie_before = Balances::free_balance(CHARLIE); + let free_dave_before = Balances::free_balance(DAVE); + + let reward_pot = Court::reward_pot(&market_id); + let tardy_or_denounced_value = 5 * MinJurorStake::get(); + let _ = Balances::deposit(&reward_pot, tardy_or_denounced_value).unwrap(); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + + let bob_slashed = RedistributionPercentage::get() * last_draws[BOB as usize].slashable; + let dave_slashed = RedistributionPercentage::get() * last_draws[DAVE as usize].slashable; + let slashed = bob_slashed + dave_slashed + tardy_or_denounced_value; + let free_alice_after = Balances::free_balance(ALICE); + assert_eq!(free_alice_after, free_alice_before + slashed / 2); + + let free_bob_after = Balances::free_balance(BOB); + assert_eq!(free_bob_after, free_bob_before - bob_slashed); + + let free_charlie_after = Balances::free_balance(CHARLIE); + assert_eq!(free_charlie_after, free_charlie_before + slashed / 2); + + let free_dave_after = Balances::free_balance(DAVE); + assert_eq!(free_dave_after, free_dave_before - dave_slashed); + + assert!(Balances::free_balance(&reward_pot).is_zero()); + }); +} + +#[test] +fn reassign_juror_stakes_rewards_treasury_if_no_winner() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::join_court(Origin::signed(BOB), amount)); + assert_ok!(Court::join_court(Origin::signed(CHARLIE), amount)); + assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + + let wrong_outcome_0 = OutcomeReport::Scalar(69u128); + let wrong_outcome_1 = OutcomeReport::Scalar(56u128); + + let draws: crate::DrawsOf = vec![ + Draw { + juror: ALICE, + weight: 1, + vote: Vote::Revealed { secret, outcome: wrong_outcome_1.clone(), salt }, + slashable: MinJurorStake::get(), + }, + Draw { + juror: BOB, + weight: 1, + vote: Vote::Revealed { secret, outcome: wrong_outcome_0.clone(), salt }, + slashable: 2 * MinJurorStake::get(), + }, + Draw { + juror: CHARLIE, + weight: 1, + vote: Vote::Revealed { secret, outcome: wrong_outcome_0, salt }, + slashable: 3 * MinJurorStake::get(), + }, + Draw { + juror: DAVE, + weight: 1, + vote: Vote::Revealed { secret, outcome: wrong_outcome_1, salt }, + slashable: 4 * MinJurorStake::get(), + }, + ] + .try_into() + .unwrap(); + let last_draws = draws.clone(); + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + run_blocks( + CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, + ); + + let mut court = >::get(market_id).unwrap(); + court.status = CourtStatus::Closed { winner: outcome, punished: false, reassigned: false }; + >::insert(market_id, court); + + assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + + let free_alice_before = Balances::free_balance(ALICE); + let free_bob_before = Balances::free_balance(BOB); + let free_charlie_before = Balances::free_balance(CHARLIE); + let free_dave_before = Balances::free_balance(DAVE); + + let treasury_account = Court::treasury_account_id(); + let free_treasury_before = Balances::free_balance(&treasury_account); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + + let alice_slashed = RedistributionPercentage::get() * last_draws[ALICE as usize].slashable; + let bob_slashed = RedistributionPercentage::get() * last_draws[BOB as usize].slashable; + let charlie_slashed = + RedistributionPercentage::get() * last_draws[CHARLIE as usize].slashable; + let dave_slashed = RedistributionPercentage::get() * last_draws[DAVE as usize].slashable; + + let slashed = bob_slashed + dave_slashed + alice_slashed + charlie_slashed; + + let free_alice_after = Balances::free_balance(ALICE); + assert_eq!(free_alice_after, free_alice_before - alice_slashed); + + let free_bob_after = Balances::free_balance(BOB); + assert_eq!(free_bob_after, free_bob_before - bob_slashed); + + let free_charlie_after = Balances::free_balance(CHARLIE); + assert_eq!(free_charlie_after, free_charlie_before - charlie_slashed); + + let free_dave_after = Balances::free_balance(DAVE); + assert_eq!(free_dave_after, free_dave_before - dave_slashed); + + assert_eq!(Balances::free_balance(&treasury_account), free_treasury_before + slashed); + }); +} + #[test] fn on_dispute_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { From b8cd91ae54d0217e05761094447a77c14ef29869 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 12:42:18 +0200 Subject: [PATCH 148/368] edit exchange API documentation --- primitives/src/traits/dispute_api.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index adc6e763f..93e961d46 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -66,8 +66,14 @@ pub trait DisputeApi { /// This can be based on the final resolution outcome of the market. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. /// + /// # Arguments + /// * `market_id` - The identifier of the market. + /// * `market` - The market data. + /// * `resolved_outcome` - The final resolution outcome of the market. + /// * `amount` - The amount of funds transferred to the dispute mechanism. + /// /// # Returns - /// Returns a negative imbalance back to the caller. + /// Returns a negative imbalance in order to transfer funds back to the caller. fn exchange( market_id: &Self::MarketId, market: &MarketOfDisputeApi, From 5d6df7961b83d185a26b3e2b69a2f1f4b9005eb3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 12:50:51 +0200 Subject: [PATCH 149/368] slash dispute bond --- zrml/prediction-markets/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 72039cb92..f722e8768 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -332,10 +332,6 @@ mod pallet { let market_status = market.status; let market_account = >::market_account(market_id); - if Self::is_dispute_bond_pending(&market_id, &market, false) { - Self::unreserve_dispute_bond(&market_id)?; - } - // Slash outstanding bonds; see // https://github.com/zeitgeistpm/runtime-audit-1/issues/34#issuecomment-1120187097 for // details. @@ -2059,6 +2055,9 @@ mod pallet { if Self::is_outsider_bond_pending(market_id, market, false) { Self::slash_outsider_bond(market_id, None)?; } + if Self::is_dispute_bond_pending(market_id, market, false) { + Self::slash_dispute_bond(market_id, None)?; + } Ok(()) } From 474d2c099f11457bc1dd7c2ee6381647ce77ed81 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 14:59:51 +0200 Subject: [PATCH 150/368] apply review suggestions --- zrml/global-disputes/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index e454d8233..28247a8c5 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -401,18 +401,15 @@ mod pallet { ensure!(gd_info.status == GdStatus::Finished, Error::::UnfinishedGlobalDispute); let winning_outcome: Option> = - >::get(market_id, &gd_info.winner_outcome); + >::take(market_id, &gd_info.winner_outcome); let mut owners_len = 0u32; // move the winning outcome info to GlobalDisputesInfo before it gets drained if let Some(outcome_info) = winning_outcome { if let Possession::Shared { owners } = &outcome_info.possession { owners_len = owners.len() as u32; } - // storage write is needed here in case, - // that the first call to purge_outcomes - // doesn't save the owners of the winning outcome - // saving this information is required to reward the winners - // this can happen if there are more than RemoveKeysLimit keys to remove + // storage write is needed in case to save the owners + // of the winning outcome before they are drained gd_info.outcome_info = outcome_info; >::insert(market_id, gd_info); } From 97e4dcccbf730f22f6665dafa85f676f133ecd91 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 15:24:32 +0200 Subject: [PATCH 151/368] use keep alive --- zrml/global-disputes/src/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 28247a8c5..4d4ae7d2a 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -303,7 +303,7 @@ mod pallet { &owner, &reward_account, voting_outcome_fee, - ExistenceRequirement::AllowDeath, + ExistenceRequirement::KeepAlive, )?; let possession = Possession::Paid { owner: owner.clone(), fee: voting_outcome_fee }; @@ -350,12 +350,13 @@ mod pallet { { match outcome_info.possession { Possession::Paid { owner, fee } => { - T::Currency::transfer( + let res = T::Currency::transfer( &Self::reward_account(&market_id), &owner, fee, ExistenceRequirement::AllowDeath, - )?; + ); + debug_assert!(res.is_ok()); } Possession::Shared { owners } => { owners_len = owners_len.saturating_add(owners.len() as u32); @@ -857,11 +858,15 @@ mod pallet { fn destroy_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { >::try_mutate(market_id, |gd_info| { let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::GlobalDisputeNotFound)?; - raw_gd_info.status = GdStatus::Destroyed; + + // in case the global dispute is already finished nothing needs to be done if let GdStatus::Active { add_outcome_end: _, vote_end } = raw_gd_info.status { T::DisputeResolution::remove_auto_resolve(market_id, vote_end); + + raw_gd_info.status = GdStatus::Destroyed; + *gd_info = Some(raw_gd_info.clone()); } - *gd_info = Some(raw_gd_info.clone()); + Ok(()) }) } From c895c08fbaeb0b04dfc6992167e57b0ca72b69db Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 15:24:54 +0200 Subject: [PATCH 152/368] cargo fmt --- zrml/global-disputes/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 4d4ae7d2a..d1578e0a7 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -858,7 +858,7 @@ mod pallet { fn destroy_global_dispute(market_id: &MarketIdOf) -> Result<(), DispatchError> { >::try_mutate(market_id, |gd_info| { let mut raw_gd_info = gd_info.as_mut().ok_or(Error::::GlobalDisputeNotFound)?; - + // in case the global dispute is already finished nothing needs to be done if let GdStatus::Active { add_outcome_end: _, vote_end } = raw_gd_info.status { T::DisputeResolution::remove_auto_resolve(market_id, vote_end); @@ -866,7 +866,7 @@ mod pallet { raw_gd_info.status = GdStatus::Destroyed; *gd_info = Some(raw_gd_info.clone()); } - + Ok(()) }) } From 991f14047090178c9addcadb4f16e55c73bd7a71 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 15:28:54 +0200 Subject: [PATCH 153/368] use function instead of raw --- zrml/global-disputes/src/tests.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 7a077cb46..21d88cfca 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -177,12 +177,7 @@ fn start_global_dispute_works() { let market = market_mock::(); Markets::::insert(market_id, &market); - let initial_items = vec![ - InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(20), owner: ALICE, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(40), owner: ALICE, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(60), owner: ALICE, amount: SETUP_AMOUNT }, - ]; + let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); let outcome_info = OutcomeInfo { @@ -251,12 +246,7 @@ fn start_global_dispute_fails_if_already_exists() { let market = market_mock::(); Markets::::insert(market_id, &market); - let initial_items = vec![ - InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(20), owner: ALICE, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(40), owner: ALICE, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(60), owner: ALICE, amount: SETUP_AMOUNT }, - ]; + let initial_items = get_initial_items(); GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()).unwrap(); assert_eq!( GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), From 56cd5328f631a689da5de504b6228aa3e70387e7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 15:51:15 +0200 Subject: [PATCH 154/368] improve documentation --- zrml/global-disputes/src/global_disputes_pallet_api.rs | 8 ++++++-- zrml/global-disputes/src/types.rs | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 80fde2c22..1c1c2ebcd 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -33,10 +33,14 @@ pub trait GlobalDisputesPalletApi { /// /// # Arguments /// - `market_id` - The id of the market. - /// - `initial_items` - The initial items (outcome, owner, amount) to add to the global dispute. + /// - `initial_items` - The initial vote options (outcome, owner, amount) + /// to add to the global dispute. One initial item consists of the vote outcome, + /// the owner of the outcome who is rewarded in case of a win, + /// and the initial vote amount for this outcome. It is not required to add any initial items. + /// In case of a duplicated outcome, the owner and amount is added to the pre-existing outcome. fn start_global_dispute( market_id: &MarketId, - initial_items: &[InitialItem], + initial_item: &[InitialItem], ) -> Result; /// Determine the winner of a global dispute. diff --git a/zrml/global-disputes/src/types.rs b/zrml/global-disputes/src/types.rs index 09ddd906f..10c83a49b 100644 --- a/zrml/global-disputes/src/types.rs +++ b/zrml/global-disputes/src/types.rs @@ -122,8 +122,12 @@ impl OldWinnerInfo } } +/// An initial vote outcome item with the outcome owner and the initial vote amount. pub struct InitialItem { + /// The outcome which is added as initial global dispute vote possibility. pub outcome: OutcomeReport, + /// The owner of the outcome. This account is rewarded in case the outcome is the winning one. pub owner: AccountId, + /// The vote amount at the start of the global dispute. pub amount: Balance, } From e261c29d760ff0eccae85f6a7eaef9a044a8fb13 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 27 Mar 2023 16:39:37 +0200 Subject: [PATCH 155/368] improve 2 outcome check --- zrml/global-disputes/src/lib.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index d1578e0a7..c9358b71f 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -18,6 +18,8 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + mod benchmarks; mod global_disputes_pallet_api; pub mod migrations; @@ -36,6 +38,7 @@ pub type InitialItemOf = crate::types::InitialItem, BalanceOf< #[frame_support::pallet] mod pallet { use crate::{types::*, weights::WeightInfoZeitgeist, GlobalDisputesPalletApi, InitialItemOf}; + use alloc::collections::BTreeSet; use core::marker::PhantomData; use frame_support::{ ensure, log, @@ -807,7 +810,18 @@ mod pallet { Error::::GlobalDisputeAlreadyExists ); - ensure!(initial_items.len() >= 2, Error::::AtLeastTwoOutcomesRequired); + let mut outcome_set = BTreeSet::new(); + // insert returns true if the outcome is already present + let outcome_count = initial_items + .iter() + .map(|item| &item.outcome) + .filter(|outcome| outcome_set.insert(outcome.clone())) + .take(2) // Limit the iterator to at most two unique outcomes + .count(); + + if outcome_count < 2 { + return Err(Error::::AtLeastTwoOutcomesRequired.into()); + } for InitialItem { outcome, owner, amount } in initial_items { ensure!(market.matches_outcome_report(outcome), Error::::OutcomeMismatch); From 80a404665c372b9922025936cc18f0e36a310e17 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 09:26:05 +0200 Subject: [PATCH 156/368] start gd requires two unqiue outcomes --- zrml/global-disputes/src/global_disputes_pallet_api.rs | 3 ++- zrml/global-disputes/src/lib.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 1c1c2ebcd..54c7e9af9 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -36,7 +36,8 @@ pub trait GlobalDisputesPalletApi { /// - `initial_items` - The initial vote options (outcome, owner, amount) /// to add to the global dispute. One initial item consists of the vote outcome, /// the owner of the outcome who is rewarded in case of a win, - /// and the initial vote amount for this outcome. It is not required to add any initial items. + /// and the initial vote amount for this outcome. + /// It is required to add at least two unique outcomes. /// In case of a duplicated outcome, the owner and amount is added to the pre-existing outcome. fn start_global_dispute( market_id: &MarketId, diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index c9358b71f..dd1164fcb 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -222,7 +222,7 @@ mod pallet { /// Sender tried to vote with an amount below a defined minimum. AmountTooLow, /// To start a global dispute, at least two outcomes are required. - AtLeastTwoOutcomesRequired, + AtLeastTwoUniqueOutcomesRequired, /// The global dispute status is invalid for this operation. InvalidGlobalDisputeStatus, /// Sender does not have enough funds for the vote on an outcome. @@ -815,12 +815,13 @@ mod pallet { let outcome_count = initial_items .iter() .map(|item| &item.outcome) + // insert returns true if the outcome was not already present (unqiue) .filter(|outcome| outcome_set.insert(outcome.clone())) .take(2) // Limit the iterator to at most two unique outcomes .count(); if outcome_count < 2 { - return Err(Error::::AtLeastTwoOutcomesRequired.into()); + return Err(Error::::AtLeastTwoUniqueOutcomesRequired.into()); } for InitialItem { outcome, owner, amount } in initial_items { From 8c468e50a7475baea62adc4f2481c18bd44a3aad Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 09:50:44 +0200 Subject: [PATCH 157/368] fix error --- zrml/global-disputes/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 21d88cfca..bed46165c 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -234,7 +234,7 @@ fn start_global_dispute_fails_if_less_than_two_outcomes() { }]; assert_eq!( GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), - Err(Error::::AtLeastTwoOutcomesRequired.into()) + Err(Error::::AtLeastTwoUniqueOutcomesRequired.into()) ); }); } From ce13fd2ed8312973742388fc21443e431aa6f201 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 09:56:43 +0200 Subject: [PATCH 158/368] add empty commit From 7b3ff21a74bb435f952bc9cb8a6735a44dbf5387 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 10:08:03 +0200 Subject: [PATCH 159/368] correct admin_destroy_market test --- zrml/prediction-markets/src/tests.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 6bdd601aa..8d76e7e49 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -619,7 +619,7 @@ fn admin_destroy_market_correctly_slashes_permissionless_market_disputed() { } #[test] -fn admin_destroy_market_correctly_unreserves_dispute_bonds() { +fn admin_destroy_market_correctly_slashes_dispute_bonds() { ExtBuilder::default().build().execute_with(|| { let end = 2; simple_create_categorical_market( @@ -674,9 +674,7 @@ fn admin_destroy_market_correctly_unreserves_dispute_bonds() { ); assert_eq!( Balances::free_balance(CHARLIE), - balance_free_before_charlie - + DisputeBond::get() - + zrml_simple_disputes::default_outcome_bond::(0) + balance_free_before_charlie + zrml_simple_disputes::default_outcome_bond::(0) ); assert_eq!( Balances::free_balance(DAVE), From e9bed9f439bf96a167469834cfd980a7ba4e026f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 10:42:45 +0200 Subject: [PATCH 160/368] revert get_resolution_outcome to on_resolution --- zrml/court/src/tests.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 6f3d0a46d..d3447859b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1473,7 +1473,7 @@ fn punish_tardy_jurors_fails_if_tardy_jurors_already_punished() { run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1493,7 +1493,7 @@ fn punish_tardy_jurors_emits_event() { run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1513,8 +1513,7 @@ fn punish_tardy_jurors_updates_court_status() { assert_eq!(court.status, CourtStatus::Open); let market = MarketCommons::market(&market_id).unwrap(); - let resolution_outcome = - Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().unwrap(); let court = >::get(market_id).unwrap(); assert_eq!( @@ -1587,7 +1586,7 @@ fn punish_tardy_jurors_slashes_tardy_jurors_only() { ); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap(); let free_alice_before = Balances::free_balance(&ALICE); let free_bob_before = Balances::free_balance(&BOB); @@ -1674,7 +1673,7 @@ fn punish_tardy_jurors_removes_tardy_jurors_from_pool() { ); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1701,7 +1700,7 @@ fn reassign_juror_stakes_fails_if_tardy_jurors_not_punished() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap(); assert_noop!( Court::reassign_juror_stakes(Origin::signed(EVE), market_id), Error::::PunishTardyJurorsFirst @@ -1718,7 +1717,7 @@ fn reassign_juror_stakes_emits_event() { run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap().unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1736,7 +1735,7 @@ fn reassign_juror_stakes_fails_if_juror_stakes_already_reassigned() { run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap().unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1757,8 +1756,7 @@ fn reassign_juror_stakes_updates_court_status() { run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); let market = MarketCommons::market(&market_id).unwrap(); - let resolution_outcome = - Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1791,7 +1789,7 @@ fn reassign_juror_stakes_removes_draws() { run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap().unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1884,7 +1882,7 @@ fn reassign_juror_stakes_decreases_active_lock() { ); let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::get_resolution_outcome(&market_id, &market).unwrap(); + let _ = Court::on_resolution(&market_id, &market).unwrap(); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); @@ -1953,8 +1951,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { ); let market = MarketCommons::market(&market_id).unwrap(); - let resolution_outcome = - Court::get_resolution_outcome(&market_id, &market).unwrap().unwrap(); + let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().unwrap(); assert_eq!(resolution_outcome, outcome); assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); From a052320c0dd22c6d8fe10c324ec5d60de6f6797e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 12:06:18 +0200 Subject: [PATCH 161/368] remove gd dependency from simple disputes --- Cargo.lock | 1 - primitives/src/traits/dispute_api.rs | 11 ++++++- runtime/common/src/lib.rs | 2 -- zrml/authorized/src/lib.rs | 8 ++++-- zrml/court/src/lib.rs | 7 +++-- zrml/prediction-markets/Cargo.toml | 1 - zrml/prediction-markets/src/lib.rs | 16 ++++++++++- zrml/prediction-markets/src/mock.rs | 2 -- zrml/simple-disputes/Cargo.toml | 4 --- zrml/simple-disputes/src/lib.rs | 39 +++++++------------------ zrml/simple-disputes/src/mock.rs | 43 ---------------------------- 11 files changed, 46 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0684752d..1f904d5d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13118,7 +13118,6 @@ dependencies = [ "sp-io", "sp-runtime", "zeitgeist-primitives", - "zrml-global-disputes", "zrml-market-commons", ] diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 2c2b058f4..bcb6734c1 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -16,10 +16,13 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . +extern crate alloc; + use crate::{ outcome_report::OutcomeReport, types::{Asset, Market}, }; +use alloc::vec::Vec; use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight}; use parity_scale_codec::MaxEncodedLen; use sp_runtime::DispatchError; @@ -34,6 +37,9 @@ type MarketOfDisputeApi = Market< Asset<::MarketId>, >; +type GlobalDisputeItemOfDisputeApi = + (OutcomeReport, ::AccountId, ::Balance); + pub trait DisputeApi { type AccountId; type Balance; @@ -103,10 +109,13 @@ pub trait DisputeApi { /// Called, when a global dispute is started. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. + /// + /// # Returns + /// Returns the initial vote outcomes with initial vote value and owner of the vote. fn on_global_dispute( market_id: &Self::MarketId, market: &MarketOfDisputeApi, - ) -> DispatchResult; + ) -> Result>, DispatchError>; /// Allow the API consumer to clear storage items of the dispute mechanism. /// This may be called, when the dispute mechanism is no longer needed. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 72c97f7e8..11891f7b9 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1062,8 +1062,6 @@ macro_rules! impl_config_traits { type OutcomeFactor = OutcomeFactor; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; - #[cfg(feature = "with-global-disputes")] - type GlobalDisputes = GlobalDisputes; type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 31d16f095..3b6f231b5 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -35,6 +35,7 @@ pub use pallet::*; #[frame_support::pallet] mod pallet { use crate::{weights::WeightInfoZeitgeist, AuthorizedPalletApi}; + use alloc::vec::Vec; use core::marker::PhantomData; use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo}, @@ -260,13 +261,16 @@ mod pallet { Ok(false) } - fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_global_dispute( + _: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); - Ok(()) + Ok(Vec::new()) } fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 6dbe09312..86541a834 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -625,12 +625,15 @@ mod pallet { Ok(false) } - fn on_global_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_global_dispute( + _: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - Ok(()) + Ok(Vec::new()) } fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { diff --git a/zrml/prediction-markets/Cargo.toml b/zrml/prediction-markets/Cargo.toml index 9ee2be18b..f12cf796f 100644 --- a/zrml/prediction-markets/Cargo.toml +++ b/zrml/prediction-markets/Cargo.toml @@ -92,7 +92,6 @@ try-runtime = [ ] with-global-disputes = [ "zrml-global-disputes", - "zrml-simple-disputes/with-global-disputes", ] [package] diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index bafd1a1f5..940565e93 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1435,6 +1435,8 @@ mod pallet { Error::::GlobalDisputeAlreadyStarted ); + let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; + let has_failed = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::has_failed(&market_id, &market)? @@ -1446,7 +1448,7 @@ mod pallet { }; ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); - match market.dispute_mechanism { + let initial_vote_outcomes = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_global_dispute(&market_id, &market)? } @@ -1458,6 +1460,18 @@ mod pallet { } }; + T::GlobalDisputes::push_voting_outcome( + &market_id, + report.outcome.clone(), + &report.by, + >::zero(), + )?; + + // push vote outcomes other than the report outcome + for (outcome, owner, bond) in initial_vote_outcomes { + T::GlobalDisputes::push_voting_outcome(&market_id, outcome, &owner, bond)?; + } + // TODO(#372): Allow court with global disputes. // ensure, that global disputes controls the resolution now // it does not end after the dispute period now, but after the global dispute end diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 894ad790e..07fd46149 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -324,8 +324,6 @@ impl zrml_simple_disputes::Config for Runtime { type OutcomeBond = OutcomeBond; type OutcomeFactor = OutcomeFactor; type DisputeResolution = prediction_markets::Pallet; - #[cfg(feature = "with-global-disputes")] - type GlobalDisputes = GlobalDisputes; type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; diff --git a/zrml/simple-disputes/Cargo.toml b/zrml/simple-disputes/Cargo.toml index 465fb3fd8..e947cc9a8 100644 --- a/zrml/simple-disputes/Cargo.toml +++ b/zrml/simple-disputes/Cargo.toml @@ -7,7 +7,6 @@ parity-scale-codec = { default-features = false, features = ["derive", "max-enco scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-runtime = { branch = "moonbeam-polkadot-v0.9.29", default-features = false, git = "https://github.com/zeitgeistpm/substrate" } zeitgeist-primitives = { default-features = false, path = "../../primitives" } -zrml-global-disputes = { default-features = false, path = "../global-disputes", optional = true } zrml-market-commons = { default-features = false, path = "../market-commons" } [dev-dependencies] @@ -37,9 +36,6 @@ std = [ try-runtime = [ "frame-support/try-runtime", ] -with-global-disputes = [ - "zrml-global-disputes", -] [package] authors = ["Zeitgeist PM "] diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 96d8f199e..9028959b6 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -54,15 +54,11 @@ mod pallet { }; use frame_system::pallet_prelude::*; use orml_traits::currency::NamedMultiReservableCurrency; - #[cfg(feature = "with-global-disputes")] - use sp_runtime::traits::Zero; use sp_runtime::{ traits::{CheckedDiv, Saturating}, DispatchError, SaturatedConversion, }; - #[cfg(feature = "with-global-disputes")] - use zrml_global_disputes::GlobalDisputesPalletApi; use zrml_market_commons::MarketCommonsPalletApi; #[pallet::config] @@ -94,14 +90,6 @@ mod pallet { #[pallet::constant] type OutcomeFactor: Get>; - /// See [`GlobalDisputesPalletApi`]. - #[cfg(feature = "with-global-disputes")] - type GlobalDisputes: GlobalDisputesPalletApi< - MarketIdOf, - Self::AccountId, - BalanceOf, - >; - /// The identifier of individual markets. type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, @@ -405,29 +393,22 @@ mod pallet { Ok(disputes.len() == T::MaxDisputes::get() as usize) } - fn on_global_dispute(_market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_global_dispute( + market_id: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - #[cfg(feature = "with-global-disputes")] - { - let disputes = >::get(_market_id); - // add report outcome to voting choices - if let Some(report) = &market.report { - T::GlobalDisputes::push_voting_outcome( - _market_id, - report.outcome.clone(), - &report.by, - >::zero(), - )?; - } - for MarketDispute { at: _, by, outcome, bond } in disputes.iter() { - T::GlobalDisputes::push_voting_outcome(_market_id, outcome.clone(), by, *bond)?; - } + let mut gd_outcomes: Vec<(OutcomeReport, Self::AccountId, Self::Balance)> = Vec::new(); + + for MarketDispute { at: _, by, outcome, bond } in >::get(market_id).iter() { + gd_outcomes.push((outcome.clone(), by.clone(), *bond)); } - Ok(()) + + Ok(gd_outcomes) } fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index ed1408094..7fa175ab4 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -40,12 +40,6 @@ use zeitgeist_primitives::{ }, }; -#[cfg(feature = "with-global-disputes")] -use zeitgeist_primitives::constants::mock::{ - GlobalDisputeLockId, GlobalDisputesPalletId, MaxGlobalDisputeVotes, MaxOwners, - MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, -}; - pub const ALICE: AccountIdTest = 0; pub const BOB: AccountIdTest = 1; pub const CHARLIE: AccountIdTest = 2; @@ -60,26 +54,6 @@ ord_parameter_types! { pub const Sudo: AccountIdTest = SUDO; } -#[cfg(feature = "with-global-disputes")] -construct_runtime!( - pub enum Runtime - where - Block = BlockTest, - NodeBlock = BlockTest, - UncheckedExtrinsic = UncheckedExtrinsicTest, - { - Balances: pallet_balances::{Call, Config, Event, Pallet, Storage}, - AssetManager: orml_currencies::{Call, Pallet, Storage}, - MarketCommons: zrml_market_commons::{Pallet, Storage}, - SimpleDisputes: zrml_simple_disputes::{Event, Pallet, Storage}, - GlobalDisputes: zrml_global_disputes::{Event, Pallet, Storage}, - System: frame_system::{Call, Config, Event, Pallet, Storage}, - Timestamp: pallet_timestamp::{Pallet}, - Tokens: orml_tokens::{Config, Event, Pallet, Storage}, - } -); - -#[cfg(not(feature = "with-global-disputes"))] construct_runtime!( pub enum Runtime where @@ -142,29 +116,12 @@ impl crate::Config for Runtime { type OutcomeBond = OutcomeBond; type OutcomeFactor = OutcomeFactor; type DisputeResolution = NoopResolution; - #[cfg(feature = "with-global-disputes")] - type GlobalDisputes = GlobalDisputes; type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; type PalletId = SimpleDisputesPalletId; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } -#[cfg(feature = "with-global-disputes")] -impl zrml_global_disputes::Config for Runtime { - type Event = (); - type MarketCommons = MarketCommons; - type Currency = Balances; - type GlobalDisputeLockId = GlobalDisputeLockId; - type GlobalDisputesPalletId = GlobalDisputesPalletId; - type MaxGlobalDisputeVotes = MaxGlobalDisputeVotes; - type MaxOwners = MaxOwners; - type MinOutcomeVoteAmount = MinOutcomeVoteAmount; - type RemoveKeysLimit = RemoveKeysLimit; - type VotingOutcomeFee = VotingOutcomeFee; - type WeightInfo = zrml_global_disputes::weights::WeightInfo; -} - impl frame_system::Config for Runtime { type AccountData = pallet_balances::AccountData; type AccountId = AccountIdTest; From 8563c257f5608c04ff038f64640c465bc20f3227 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 12:18:53 +0200 Subject: [PATCH 162/368] fix clippy --- zrml/global-disputes/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index dd1164fcb..577d6f4bf 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -814,9 +814,8 @@ mod pallet { // insert returns true if the outcome is already present let outcome_count = initial_items .iter() - .map(|item| &item.outcome) // insert returns true if the outcome was not already present (unqiue) - .filter(|outcome| outcome_set.insert(outcome.clone())) + .filter(|item| outcome_set.insert(item.outcome.clone())) .take(2) // Limit the iterator to at most two unique outcomes .count(); From 978b425d2827a24f3173790be4a22f4136e01eb2 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 12:56:19 +0200 Subject: [PATCH 163/368] use appealed outcomes for global dispute --- zrml/court/src/lib.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d242ed249..7542fdd5a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1489,16 +1489,40 @@ mod pallet { Ok(has_failed) } - fn on_global_dispute(market_id: &Self::MarketId, market: &MarketOf) -> Result, DispatchError> { + fn on_global_dispute( + market_id: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); + let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + + let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; + let oracle_outcome = &report.outcome; + + let gd_outcomes = court + .appeals + .iter() + // oracle outcome is added by pm pallet + .filter(|a| &a.appealed_outcome != oracle_outcome) + .map(|a| { + ( + a.appealed_outcome.clone(), + // we have no better global dispute outcome owner + Self::treasury_account_id(), + // initial vote amount + >::zero(), + ) + }) + .collect::>(); + >::remove(market_id); >::remove(market_id); - Ok(Vec::new()) + Ok(gd_outcomes) } fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { From 009039dee9000c9ac2dc96b9552bf7994ee81fe3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 28 Mar 2023 13:14:50 +0200 Subject: [PATCH 164/368] fix tests and benchmarks --- zrml/global-disputes/src/benchmarks.rs | 7 ++++++- zrml/global-disputes/src/tests.rs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/zrml/global-disputes/src/benchmarks.rs b/zrml/global-disputes/src/benchmarks.rs index d0c4b195d..1083a2eb0 100644 --- a/zrml/global-disputes/src/benchmarks.rs +++ b/zrml/global-disputes/src/benchmarks.rs @@ -74,10 +74,15 @@ benchmarks! { deposit::(&caller); let mut initial_items: Vec> = Vec::new(); + initial_items.push(InitialItem { + outcome: outcome.clone(), + owner: caller.clone(), + amount: 1_000_000_000u128.saturated_into(), + }); for i in 1..=o { let owner = account("outcomes_owner", i, 0); initial_items.push(InitialItem { - outcome: outcome.clone(), + outcome: OutcomeReport::Scalar(i.saturated_into()), owner, amount: 1_000_000_000u128.saturated_into(), }); diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index bed46165c..ce5cabfc3 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -263,9 +263,14 @@ fn start_global_dispute_fails_if_max_owner_reached() { Markets::::insert(market_id, &market); let mut initial_items = Vec::new(); + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(0), + owner: 0u128, + amount: SETUP_AMOUNT, + }); for i in 0..MaxOwners::get() + 1 { initial_items.push(InitialItem { - outcome: OutcomeReport::Scalar(0), + outcome: OutcomeReport::Scalar(42), owner: i.into(), amount: SETUP_AMOUNT, }); From c432ef467b5c19fd53254db2353bac03ccc847d6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 29 Mar 2023 09:18:35 +0200 Subject: [PATCH 165/368] modify appeal bond formula --- runtime/battery-station/src/parameters.rs | 3 +-- runtime/common/src/lib.rs | 1 - runtime/zeitgeist/src/parameters.rs | 3 +-- zrml/court/src/lib.rs | 12 +++++------- zrml/court/src/mock.rs | 3 +-- zrml/court/src/tests.rs | 13 ++++++++++++- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 6dcefad9f..1bf3146ce 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -77,11 +77,10 @@ parameter_types! { // Court /// (Slashable) Bond that is provided for overriding the last appeal. + /// This bond increases exponentially with the number of appeals. /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` /// was deposited. pub const AppealBond: Balance = 5 * BASE; - /// `AppealBond` is increased by this factor after every new appeal. - pub const AppealBondFactor: Balance = 2 * BASE; /// Pallet identifier, mainly used for named balance reserves. pub const CourtPalletId: PalletId = COURT_PALLET_ID; /// The time in which the jurors can cast their secret vote. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 97bf9ef3b..c9bce2c57 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -948,7 +948,6 @@ macro_rules! impl_config_traits { impl zrml_court::Config for Runtime { type AppealBond = AppealBond; - type AppealBondFactor = AppealBondFactor; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index b473e9640..221b0c362 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -77,11 +77,10 @@ parameter_types! { // Court /// (Slashable) Bond that is provided for overriding the last appeal. + /// This bond increases exponentially with the number of appeals. /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` /// was deposited. pub const AppealBond: Balance = 2000 * BASE; - /// `AppealBond` is increased by this factor after every new appeal. - pub const AppealBondFactor: Balance = 200 * BASE; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. pub const CourtPalletId: PalletId = COURT_PALLET_ID; /// The time in which the jurors can cast their secret vote. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7542fdd5a..43e45513c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -77,14 +77,10 @@ mod pallet { #[pallet::config] pub trait Config: frame_system::Config { /// The required base bond in order to get an appeal initiated. + /// This bond increases exponentially with the number of appeals. #[pallet::constant] type AppealBond: Get>; - /// The additional amount of currency that must be bonded when creating a subsequent - /// appeal. - #[pallet::constant] - type AppealBondFactor: Get>; - /// The time in which the jurors can cast their secret vote. #[pallet::constant] type CourtVotePeriod: Get; @@ -177,6 +173,8 @@ mod pallet { // Weight used to increase the number of jurors for subsequent appeals // of the same market const SUBSEQUENT_JURORS_FACTOR: usize = 2; + // Basis used to increase the bond for subsequent appeals of the same market + const APPEAL_BOND_BASIS: u32 = 2; pub(crate) type AccountIdOf = ::AccountId; pub(crate) type BalanceOf = <::Currency as Currency>>::Balance; @@ -1545,8 +1543,8 @@ mod pallet { where T: Config, { - T::AppealBond::get().saturating_add( - T::AppealBondFactor::get().saturating_mul(n.saturated_into::().into()), + T::AppealBond::get().saturating_mul( + (APPEAL_BOND_BASIS.saturating_pow(n as u32)).saturated_into::>(), ) } } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 1820688e8..3d42578d8 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -32,7 +32,7 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - AppealBond, AppealBondFactor, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, + AppealBond, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, RequestInterval, TardySlashPercentage, BASE, @@ -133,7 +133,6 @@ impl DisputeResolutionApi for MockResolution { impl crate::Config for Runtime { type AppealBond = AppealBond; - type AppealBondFactor = AppealBondFactor; type CourtLockId = CourtLockId; type Currency = Balances; type CourtVotePeriod = CourtVotePeriod; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index d3447859b..5fd851d81 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -37,7 +37,7 @@ use test_case::test_case; use zeitgeist_primitives::{ constants::{ mock::{ - CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, + AppealBond, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxJurors, MinJurorStake, RedistributionPercentage, RequestInterval, TardySlashPercentage, }, @@ -2250,6 +2250,17 @@ fn check_necessary_jurors_weight() { }); } +#[test] +fn check_appeal_bond() { + ExtBuilder::default().build().execute_with(|| { + let appeal_bond = AppealBond::get(); + assert_eq!(crate::default_appeal_bond::(0usize), appeal_bond); + assert_eq!(crate::default_appeal_bond::(1usize), 2 * appeal_bond); + assert_eq!(crate::default_appeal_bond::(2usize), 4 * appeal_bond); + assert_eq!(crate::default_appeal_bond::(3usize), 8 * appeal_bond); + }); +} + fn prepare_draws(market_id: &crate::MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { let mut draws: crate::DrawsOf = vec![].try_into().unwrap(); for (i, (outcome_index, weight)) in outcomes_with_weights.iter().enumerate() { From b7c01274a65ee6215a59db9ef451099a9c995add Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 29 Mar 2023 10:01:34 +0200 Subject: [PATCH 166/368] remove slash percentages --- primitives/src/constants/mock.rs | 4 --- runtime/battery-station/src/parameters.rs | 6 ----- runtime/common/src/lib.rs | 3 --- runtime/zeitgeist/src/parameters.rs | 6 ----- zrml/court/src/lib.rs | 23 +++------------- zrml/court/src/mock.rs | 10 +++---- zrml/court/src/tests.rs | 33 ++++++++--------------- zrml/prediction-markets/src/mock.rs | 27 ++++++++----------- 8 files changed, 29 insertions(+), 83 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index df0eefec9..d97b03738 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -25,7 +25,6 @@ use crate::{ }; use frame_support::{parameter_types, traits::LockIdentifier, PalletId}; use orml_traits::parameter_type_with_key; -use sp_runtime::Percent; // Authorized parameter_types! { @@ -43,13 +42,10 @@ parameter_types! { pub const CourtAggregationPeriod: BlockNumber = 32; pub const CourtAppealPeriod: BlockNumber = 16; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; - pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); pub const MaxAppeals: u32 = 4; pub const MaxDraws: u32 = 191; pub const MaxJurors: u32 = 1000; pub const MinJurorStake: Balance = 50 * CENT; - pub const RedistributionPercentage: Percent = Percent::from_percent(10); - pub const TardySlashPercentage: Percent = Percent::from_percent(20); } // Global disputes parameters diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 1bf3146ce..725177b75 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -91,8 +91,6 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = BLOCKS_PER_DAY; /// The court lock identifier. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; - /// The slash percentage if a secret vote gets revealed during the voting period. - pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 4; /// The maximum number of randomly selected jurors for a dispute. @@ -101,12 +99,8 @@ parameter_types! { pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. pub const MinJurorStake: Balance = 1000 * BASE; - /// The percentage that is slashed if a juror did not vote for the plurality outcome. - pub const RedistributionPercentage: Percent = Percent::from_percent(10); /// The interval for requesting multiple court votes at once. pub const RequestInterval: BlockNumber = 7 * BLOCKS_PER_DAY; - /// The percentage that is being slashed from the juror's stake. - pub const TardySlashPercentage: Percent = Percent::from_percent(20); // Democracy /// How often (in blocks) new public referenda are launched. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index c9bce2c57..dde77a862 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -954,7 +954,6 @@ macro_rules! impl_config_traits { type CourtLockId = CourtLockId; type CourtPalletId = CourtPalletId; type Currency = Balances; - type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type MarketCommons = MarketCommons; @@ -963,9 +962,7 @@ macro_rules! impl_config_traits { type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; type Random = RandomnessCollectiveFlip; - type RedistributionPercentage = RedistributionPercentage; type RequestInterval = RequestInterval; - type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 221b0c362..5dc5aa711 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -91,8 +91,6 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = BLOCKS_PER_DAY; /// The lock identifier for the court votes. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; - /// The slash percentage if a secret vote gets revealed during the voting period. - pub const DenounceSlashPercentage: Percent = Percent::from_percent(100); /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 4; /// The maximum number of randomly selected jurors for a dispute. @@ -101,12 +99,8 @@ parameter_types! { pub const MaxJurors: u32 = 10_000; /// The minimum stake a user needs to reserve to become a juror. pub const MinJurorStake: Balance = 1000 * BASE; - /// The percentage that is slashed if a juror did not vote for the plurality outcome. - pub const RedistributionPercentage: Percent = Percent::from_percent(10); /// The interval for requesting multiple court votes at once. pub const RequestInterval: BlockNumber = 7 * BLOCKS_PER_DAY; - /// The percentage that is being slashed from the juror's stake. - pub const TardySlashPercentage: Percent = Percent::from_percent(20); // Democracy /// How often (in blocks) new public referenda are launched. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 43e45513c..15e5d914a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -66,7 +66,7 @@ mod pallet { use rand_chacha::ChaCha20Rng; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup, Zero}, - DispatchError, Percent, SaturatedConversion, + DispatchError, SaturatedConversion, }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, @@ -106,10 +106,6 @@ mod pallet { + NamedReservableCurrency + LockableCurrency; - /// The slash percentage if a secret vote gets revealed during the voting period. - #[pallet::constant] - type DenounceSlashPercentage: Get; - /// The functionality to allow controlling the markets resolution time. type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, @@ -150,14 +146,6 @@ mod pallet { #[pallet::constant] type RequestInterval: Get; - /// The percentage that is slashed if a juror did not vote for the plurality outcome. - #[pallet::constant] - type RedistributionPercentage: Get; - - /// The percentage that is being slashed from the juror's stake in case she is tardy. - #[pallet::constant] - type TardySlashPercentage: Get; - /// Slashed funds are send to the treasury #[pallet::constant] type TreasuryPalletId: Get; @@ -649,8 +637,7 @@ mod pallet { }; let reward_pot = Self::reward_pot(&market_id); - let slash = T::DenounceSlashPercentage::get() * draw.slashable; - let (imbalance, missing) = T::Currency::slash(&juror, slash); + let (imbalance, missing) = T::Currency::slash(&juror, draw.slashable); debug_assert!(missing.is_zero(), "Could not slash all of the amount."); T::Currency::resolve_creating(&reward_pot, imbalance); @@ -888,8 +875,7 @@ mod pallet { let mut jurors = JurorPool::::get(); let reward_pot = Self::reward_pot(&market_id); let mut slash_and_remove_juror = |ai: &T::AccountId, slashable: BalanceOf| { - let slash = T::TardySlashPercentage::get() * slashable; - let (imbalance, missing) = T::Currency::slash(ai, slash); + let (imbalance, missing) = T::Currency::slash(ai, slashable); debug_assert!( missing.is_zero(), "Could not slash all of the amount for juror {:?}.", @@ -1230,8 +1216,7 @@ mod pallet { if outcome == winner_outcome { winners.push(juror); } else { - let slash = T::RedistributionPercentage::get() * *slashable; - let (imb, missing) = T::Currency::slash(juror, slash); + let (imb, missing) = T::Currency::slash(juror, *slashable); total_incentives.subsume(imb); debug_assert!( missing.is_zero(), diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 3d42578d8..823a62672 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -32,10 +32,9 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - AppealBond, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, - CourtLockId, CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, MaxAppeals, MaxDraws, - MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RedistributionPercentage, - RequestInterval, TardySlashPercentage, BASE, + AppealBond, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, + CourtPalletId, CourtVotePeriod, MaxAppeals, MaxDraws, MaxJurors, MaxReserves, + MinJurorStake, MinimumPeriod, PmPalletId, RequestInterval, BASE, }, traits::DisputeResolutionApi, types::{ @@ -138,7 +137,6 @@ impl crate::Config for Runtime { type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; - type DenounceSlashPercentage = DenounceSlashPercentage; type DisputeResolution = MockResolution; type Event = Event; type MarketCommons = MarketCommons; @@ -148,9 +146,7 @@ impl crate::Config for Runtime { type MinJurorStake = MinJurorStake; type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; - type RedistributionPercentage = RedistributionPercentage; type RequestInterval = RequestInterval; - type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 5fd851d81..37dd4f295 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -38,8 +38,7 @@ use zeitgeist_primitives::{ constants::{ mock::{ AppealBond, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, - DenounceSlashPercentage, MaxAppeals, MaxJurors, MinJurorStake, - RedistributionPercentage, RequestInterval, TardySlashPercentage, + MaxAppeals, MaxJurors, MinJurorStake, RequestInterval, }, BASE, }, @@ -903,7 +902,7 @@ fn denounce_vote_works() { }); let free_alice_after = Balances::free_balance(ALICE); - let slash = DenounceSlashPercentage::get() * old_draws[0].slashable; + let slash = old_draws[0].slashable; assert!(!slash.is_zero()); assert_eq!(free_alice_after, free_alice_before - slash); @@ -1598,27 +1597,18 @@ fn punish_tardy_jurors_slashes_tardy_jurors_only() { let free_alice_after = Balances::free_balance(&ALICE); assert_ne!(free_alice_after, free_alice_before); - assert_eq!( - free_alice_after, - free_alice_before - TardySlashPercentage::get() * old_draws[ALICE as usize].slashable - ); + assert_eq!(free_alice_after, free_alice_before - old_draws[ALICE as usize].slashable); let free_bob_after = Balances::free_balance(&BOB); assert_ne!(free_bob_after, free_bob_before); - assert_eq!( - free_bob_after, - free_bob_before - TardySlashPercentage::get() * old_draws[BOB as usize].slashable - ); + assert_eq!(free_bob_after, free_bob_before - old_draws[BOB as usize].slashable); let free_charlie_after = Balances::free_balance(&CHARLIE); assert_eq!(free_charlie_after, free_charlie_before); let free_dave_after = Balances::free_balance(&DAVE); assert_ne!(free_dave_after, free_dave_before); - assert_eq!( - free_dave_after, - free_dave_before - TardySlashPercentage::get() * old_draws[DAVE as usize].slashable - ); + assert_eq!(free_dave_after, free_dave_before - old_draws[DAVE as usize].slashable); let free_eve_after = Balances::free_balance(&EVE); assert_eq!(free_eve_after, free_eve_before); @@ -1967,8 +1957,8 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); - let bob_slashed = RedistributionPercentage::get() * last_draws[BOB as usize].slashable; - let dave_slashed = RedistributionPercentage::get() * last_draws[DAVE as usize].slashable; + let bob_slashed = last_draws[BOB as usize].slashable; + let dave_slashed = last_draws[DAVE as usize].slashable; let slashed = bob_slashed + dave_slashed + tardy_or_denounced_value; let free_alice_after = Balances::free_balance(ALICE); assert_eq!(free_alice_after, free_alice_before + slashed / 2); @@ -2058,11 +2048,10 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); - let alice_slashed = RedistributionPercentage::get() * last_draws[ALICE as usize].slashable; - let bob_slashed = RedistributionPercentage::get() * last_draws[BOB as usize].slashable; - let charlie_slashed = - RedistributionPercentage::get() * last_draws[CHARLIE as usize].slashable; - let dave_slashed = RedistributionPercentage::get() * last_draws[DAVE as usize].slashable; + let alice_slashed = last_draws[ALICE as usize].slashable; + let bob_slashed = last_draws[BOB as usize].slashable; + let charlie_slashed = last_draws[CHARLIE as usize].slashable; + let dave_slashed = last_draws[DAVE as usize].slashable; let slashed = bob_slashed + dave_slashed + alice_slashed + charlie_slashed; diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index b654e707c..9a5c236a9 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -38,18 +38,17 @@ use sp_runtime::{ use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; use zeitgeist_primitives::{ constants::mock::{ - AppealBond, AppealBondFactor, AuthorizedPalletId, BalanceFractionalDecimals, - BlockHashCount, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, - CourtPalletId, CourtVotePeriod, DenounceSlashPercentage, ExistentialDeposit, - ExistentialDeposits, ExitFee, GetNativeCurrencyId, LiquidityMiningPalletId, MaxAppeals, - MaxApprovals, MaxAssets, MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDraws, - MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, - MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, - MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, - MinJurorStake, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, - MinimumPeriod, OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, - RedistributionPercentage, RequestInterval, SimpleDisputesPalletId, SwapsPalletId, - TardySlashPercentage, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, + AppealBond, AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, + CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, + CourtVotePeriod, ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, + LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, + MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, + MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, + MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, + MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, + MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, + OutsiderBond, PmPalletId, RequestInterval, SimpleDisputesPalletId, SwapsPalletId, + TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -280,14 +279,12 @@ impl zrml_authorized::Config for Runtime { impl zrml_court::Config for Runtime { type AppealBond = AppealBond; - type AppealBondFactor = AppealBondFactor; type DisputeResolution = prediction_markets::Pallet; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; type CourtLockId = CourtLockId; type Currency = Balances; - type DenounceSlashPercentage = DenounceSlashPercentage; type Event = Event; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; @@ -296,9 +293,7 @@ impl zrml_court::Config for Runtime { type MinJurorStake = MinJurorStake; type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; - type RedistributionPercentage = RedistributionPercentage; type RequestInterval = RequestInterval; - type TardySlashPercentage = TardySlashPercentage; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } From fdfa079b23972c56d8807c5296aa9cf0c0f1334e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 29 Mar 2023 11:27:18 +0200 Subject: [PATCH 167/368] test default toolchain --- scripts/tests/misc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/misc.sh b/scripts/tests/misc.sh index 9431faf60..85055edea 100755 --- a/scripts/tests/misc.sh +++ b/scripts/tests/misc.sh @@ -18,7 +18,7 @@ test_package_with_feature primitives std $rustflags no_runtime_benchmarks=('court' 'market-commons' 'rikiddo') -cargo test --package zeitgeist-runtime --lib -- --nocapture +cargo +nightly-2022-09-24 test --package zeitgeist-runtime --lib -- --nocapture # TODO(#848): Delete when feature "with-global-dispute" is removed cargo test -p zrml-prediction-markets --features with-global-disputes,parachain From 718b99b9c313b2d24abec516fa606a46219300b3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 30 Mar 2023 13:53:25 +0200 Subject: [PATCH 168/368] benchmark court --- primitives/src/constants/mock.rs | 4 +- runtime/battery-station/src/parameters.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/court/src/benchmarks.rs | 483 +++++++++++++++++++++- zrml/court/src/lib.rs | 38 +- zrml/court/src/tests.rs | 19 + zrml/court/src/weights.rs | 155 ++++++- 7 files changed, 642 insertions(+), 61 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index d97b03738..3d8f61437 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -43,8 +43,8 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = 16; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const MaxAppeals: u32 = 4; - pub const MaxDraws: u32 = 191; - pub const MaxJurors: u32 = 1000; + pub const MaxDraws: u32 = 47; + pub const MaxJurors: u32 = 1_000; pub const MinJurorStake: Balance = 50 * CENT; } diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 725177b75..652131e5a 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -96,7 +96,7 @@ parameter_types! { /// The maximum number of randomly selected jurors for a dispute. pub const MaxDraws: u32 = 47; /// The maximum number of jurors that can be registered. - pub const MaxJurors: u32 = 10_000; + pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. pub const MinJurorStake: Balance = 1000 * BASE; /// The interval for requesting multiple court votes at once. diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 5dc5aa711..43463e7b3 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -96,7 +96,7 @@ parameter_types! { /// The maximum number of randomly selected jurors for a dispute. pub const MaxDraws: u32 = 47; /// The maximum number of jurors that can be registered. - pub const MaxJurors: u32 = 10_000; + pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. pub const MinJurorStake: Balance = 1000 * BASE; /// The interval for requesting multiple court votes at once. diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 7f847e747..70b7bf3ee 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -22,13 +22,63 @@ )] #![cfg(feature = "runtime-benchmarks")] -#[cfg(test)] -use crate::Pallet as Court; -use crate::{BalanceOf, Call, Config, Pallet}; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +extern crate alloc; +use crate::{ + types::{CourtStatus, Draw, JurorInfo, JurorPoolItem, Vote}, + AppealInfo, BalanceOf, Call, Config, Courts, Draws, JurorPool, Jurors, MarketOf, + Pallet as Court, Pallet, RequestBlock, +}; +use alloc::vec; +use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; +use sp_runtime::{ + traits::{Bounded, Hash, Saturating, StaticLookup, Zero}, + SaturatedConversion, +}; +use zeitgeist_primitives::{ + traits::{DisputeApi, DisputeResolutionApi}, + types::{ + Asset, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, + }, +}; +use zrml_market_commons::MarketCommonsPalletApi; + +const ORACLE_REPORT: OutcomeReport = OutcomeReport::Scalar(u128::MAX); + +fn get_market() -> MarketOf +where + T: Config, +{ + Market { + base_asset: Asset::Ztg, + creation: MarketCreation::Permissionless, + creator_fee: 0, + creator: account("creator", 0, 0), + market_type: MarketType::Scalar(0..=100), + dispute_mechanism: MarketDisputeMechanism::Court, + metadata: vec![], + oracle: account("oracle", 0, 0), + period: MarketPeriod::Block( + 0u64.saturated_into::()..100u64.saturated_into::(), + ), + deadlines: Deadlines { + grace_period: 1_u64.saturated_into::(), + oracle_duration: 1_u64.saturated_into::(), + dispute_duration: 1_u64.saturated_into::(), + }, + report: Some(Report { + at: 1u64.saturated_into::(), + by: account("oracle", 0, 0), + outcome: ORACLE_REPORT, + }), + resolved_outcome: None, + status: MarketStatus::Disputed, + scoring_rule: ScoringRule::CPMM, + bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None }, + } +} fn deposit(caller: &T::AccountId) where @@ -37,13 +87,432 @@ where let _ = T::Currency::deposit_creating(caller, BalanceOf::::max_value()); } +fn fill_pool(number: u32) -> Result<(), &'static str> +where + T: Config, +{ + let mut jurors = >::get(); + let min_amount = T::MinJurorStake::get(); + let max_amount = min_amount + min_amount + BalanceOf::::from(number); + for i in 0..number { + let juror: T::AccountId = account("juror", i, 0); + let stake = max_amount - BalanceOf::::from(i); + >::insert( + juror.clone(), + JurorInfo { stake, active_lock: >::zero() }, + ); + let consumed_stake = BalanceOf::::zero(); + let pool_item = JurorPoolItem { stake, juror: juror.clone(), consumed_stake }; + match jurors + .binary_search_by_key(&(stake, &juror), |pool_item| (pool_item.stake, &pool_item.juror)) + { + Ok(_) => panic!("Juror already in pool"), + Err(index) => jurors.try_insert(index, pool_item).unwrap(), + }; + } + >::put(jurors); + Ok(()) +} + +fn join_with_min_stake(caller: &T::AccountId) -> Result<(), &'static str> +where + T: Config, +{ + let stake = T::MinJurorStake::get(); + deposit::(caller); + Court::::join_court(RawOrigin::Signed(caller.clone()).into(), stake)?; + Ok(()) +} + +fn setup_court() -> Result, &'static str> +where + T: Config, +{ + >::set_block_number(1u64.saturated_into::()); + + let now = >::block_number(); + >::put(now + 1u64.saturated_into::()); + + let market_id = T::MarketCommons::push_market(get_market::()).unwrap(); + Court::::on_dispute(&market_id, &get_market::()).unwrap(); + + Ok(market_id) +} + +fn fill_draws(market_id: crate::MarketIdOf, number: u32) -> Result<(), &'static str> +where + T: Config, +{ + // remove last random selections of on_dispute + >::remove(market_id); + let mut draws = >::get(market_id); + for i in 0..number { + let juror = account("juror", i, 0); + deposit::(&juror); + >::insert( + &juror, + JurorInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get() }, + ); + let draw = + Draw { juror, vote: Vote::Drawn, weight: 1u32, slashable: T::MinJurorStake::get() }; + draws.try_push(draw).unwrap(); + } + >::insert(market_id, draws); + Ok(()) +} + benchmarks! { join_court { + let j in 0..(T::MaxJurors::get() - 1); + + fill_pool::(j)?; + + let caller: T::AccountId = whitelisted_caller(); + join_with_min_stake::(&caller)?; + + let new_stake = T::MinJurorStake::get() + .saturating_add(1u128.saturated_into::>()); + }: _(RawOrigin::Signed(caller), new_stake) + + prepare_exit_court { + let j in 0..(T::MaxJurors::get() - 1); + + fill_pool::(j)?; + + let caller: T::AccountId = whitelisted_caller(); + join_with_min_stake::(&caller)?; + }: _(RawOrigin::Signed(caller)) + + exit_court_remove { + let j in 0..(T::MaxJurors::get() - 1); + + fill_pool::(j)?; + + let caller: T::AccountId = whitelisted_caller(); + join_with_min_stake::(&caller)?; + + Court::::prepare_exit_court(RawOrigin::Signed(caller.clone()).into())?; + + >::mutate(caller.clone(), |prev_juror_info| { + prev_juror_info.as_mut().unwrap().active_lock = >::zero(); + }); + + let juror = T::Lookup::unlookup(caller.clone()); + }: exit_court(RawOrigin::Signed(caller), juror) + + exit_court_set { + let j in 0..(T::MaxJurors::get() - 1); + + fill_pool::(j)?; + + let caller: T::AccountId = whitelisted_caller(); + join_with_min_stake::(&caller)?; + + Court::::prepare_exit_court(RawOrigin::Signed(caller.clone()).into())?; + + >::mutate(caller.clone(), |prev_juror_info| { + prev_juror_info.as_mut().unwrap().active_lock = T::MinJurorStake::get(); + }); + + let juror = T::Lookup::unlookup(caller.clone()); + }: exit_court(RawOrigin::Signed(caller), juror) + + vote { + let d in 1..T::MaxDraws::get(); + + fill_pool::(T::MaxJurors::get() - 1)?; + + let caller: T::AccountId = whitelisted_caller(); + let market_id = setup_court::()?; + + let court = >::get(market_id).unwrap(); + let pre_vote_end = court.periods.pre_vote_end; + + fill_draws::(market_id, d)?; + + let mut draws = >::get(market_id); + let draws_len = draws.len(); + draws[draws_len.saturating_sub(1usize)] = Draw { + juror: caller.clone(), + vote: Vote::Drawn, + weight: 1u32, + slashable: >::zero() + }; + >::insert(market_id, draws); + + >::set_block_number(pre_vote_end + 1u64.saturated_into::()); + + let secret_vote = Default::default(); + }: _(RawOrigin::Signed(caller), market_id, secret_vote) + + denounce_vote { + let d in 1..T::MaxDraws::get(); + + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; + + let caller: T::AccountId = whitelisted_caller(); + let market_id = setup_court::()?; + + let court = >::get(market_id).unwrap(); + let pre_vote_end = court.periods.pre_vote_end; + + fill_draws::(market_id, d)?; + + let salt = Default::default(); + let outcome = OutcomeReport::Scalar(0u128); + let denounced_juror: T::AccountId = account("denounced_juror", 0, 0); + join_with_min_stake::(&denounced_juror)?; + >::insert(&denounced_juror, JurorInfo { + stake: T::MinJurorStake::get(), + active_lock: T::MinJurorStake::get(), + }); + let denounced_juror_unlookup = T::Lookup::unlookup(denounced_juror.clone()); + let secret = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); + + let mut draws = >::get(market_id); + let draws_len = draws.len(); + draws[draws_len.saturating_sub(1usize)] = Draw { + juror: denounced_juror, + vote: Vote::Secret { secret }, + weight: 1u32, + slashable: T::MinJurorStake::get(), + }; + >::insert(market_id, draws); + + >::set_block_number(pre_vote_end + 1u64.saturated_into::()); + }: _(RawOrigin::Signed(caller), market_id, denounced_juror_unlookup, outcome, salt) + + reveal_vote { + let d in 1..T::MaxDraws::get(); + + fill_pool::(T::MaxJurors::get() - 1)?; + + let caller: T::AccountId = whitelisted_caller(); + let market_id = setup_court::()?; + + let court = >::get(market_id).unwrap(); + let vote_end = court.periods.vote_end; + + fill_draws::(market_id, d)?; + + let salt = Default::default(); + let outcome = OutcomeReport::Scalar(0u128); + join_with_min_stake::(&caller)?; + >::insert(&caller, JurorInfo { + stake: T::MinJurorStake::get(), + active_lock: T::MinJurorStake::get(), + }); + let secret = T::Hashing::hash_of(&(caller.clone(), outcome.clone(), salt)); + + let mut draws = >::get(market_id); + let draws_len = draws.len(); + draws[draws_len.saturating_sub(1usize)] = Draw { + juror: caller.clone(), + vote: Vote::Secret { secret }, + weight: 1u32, + slashable: T::MinJurorStake::get(), + }; + >::insert(market_id, draws); + + >::set_block_number(vote_end + 1u64.saturated_into::()); + }: _(RawOrigin::Signed(caller), market_id, outcome, salt) + + appeal { + // from 47 because in the last appeal round we need at least 47 jurors + let j in 47..T::MaxJurors::get(); + let a in 0..(T::MaxAppeals::get() - 2); + let r in 0..62; + let f in 0..62; + + let necessary_jurors_weight = Court::::necessary_jurors_weight((T::MaxAppeals::get() - 1) as usize); + debug_assert!(necessary_jurors_weight == 47usize); + fill_pool::(j)?; + let caller: T::AccountId = whitelisted_caller(); deposit::(&caller); - let amount = T::MinJurorStake::get(); - }: _(RawOrigin::Signed(caller), amount) + let market_id = setup_court::()?; + + let mut court = >::get(market_id).unwrap(); + let appeal_end = court.periods.appeal_end; + for i in 0..r { + let market_id_i = (i + 100).saturated_into::>(); + T::DisputeResolution::add_auto_resolve(&market_id_i, appeal_end).unwrap(); + } + T::DisputeResolution::add_auto_resolve(&market_id, appeal_end).unwrap(); + + let aggregation_end = court.periods.aggregation_end; + for i in 0..a { + let appeal_info = AppealInfo { + backer: account("backer", i, 0), + bond: crate::default_appeal_bond::(i as usize), + appealed_outcome: OutcomeReport::Scalar(0u128), + }; + court.appeals.try_push(appeal_info).unwrap(); + } + >::insert(market_id, court); + + let salt = Default::default(); + // remove last random selections of on_dispute + >::remove(market_id); + let mut draws = >::get(market_id); + let draws_len = Court::::necessary_jurors_weight(a as usize) as u32; + for i in 0..draws_len { + let juror: T::AccountId = account("juror", i, 0); + >::insert(&juror, JurorInfo { + stake: T::MinJurorStake::get(), + active_lock: T::MinJurorStake::get(), + }); + let outcome = OutcomeReport::Scalar(i as u128); + let secret = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let draw = + Draw { + juror, + vote: Vote::Revealed { secret, outcome, salt }, + weight: 1u32, + slashable: >::zero() + }; + draws.try_push(draw).unwrap(); + } + >::insert(market_id, draws); + + >::set_block_number(aggregation_end + 1u64.saturated_into::()); + let now = >::block_number(); + >::put(now + 1u64.saturated_into::()); + + let new_resolve_at = >::get() + + T::CourtVotePeriod::get() + + T::CourtAggregationPeriod::get() + + T::CourtAppealPeriod::get(); + for i in 0..f { + let market_id_i = (i + 100).saturated_into::>(); + T::DisputeResolution::add_auto_resolve(&market_id_i, new_resolve_at).unwrap(); + } + }: _(RawOrigin::Signed(caller), market_id) + verify { + let court = >::get(market_id).unwrap(); + assert_eq!(court.periods.appeal_end, new_resolve_at); + } + + back_global_dispute { + let d in 1..T::MaxDraws::get(); + let r in 0..62; + + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; + + let caller: T::AccountId = whitelisted_caller(); + deposit::(&caller); + let market_id = setup_court::()?; + + let mut court = >::get(market_id).unwrap(); + let appeal_end = court.periods.appeal_end; + for i in 0..r { + let market_id_i = i.saturated_into::>(); + T::DisputeResolution::add_auto_resolve(&market_id_i, appeal_end).unwrap(); + } + + T::DisputeResolution::add_auto_resolve(&market_id, appeal_end).unwrap(); + + let aggregation_end = court.periods.aggregation_end; + for i in 0..(T::MaxAppeals::get() - 1) { + let appeal_info = AppealInfo { + backer: account("backer", i, 0), + bond: crate::default_appeal_bond::(i as usize), + appealed_outcome: OutcomeReport::Scalar(0u128), + }; + court.appeals.try_push(appeal_info).unwrap(); + } + >::insert(market_id, court); + + let salt = Default::default(); + // remove last random selections of on_dispute + >::remove(market_id); + let mut draws = >::get(market_id); + for i in 0..d { + let juror: T::AccountId = account("juror", i, 0); + >::insert(&juror, JurorInfo { + stake: T::MinJurorStake::get(), + active_lock: T::MinJurorStake::get(), + }); + let outcome = OutcomeReport::Scalar(i as u128); + let secret = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let draw = + Draw { + juror, + vote: Vote::Revealed { secret, outcome, salt }, + weight: 1u32, + slashable: T::MinJurorStake::get(), + }; + draws.try_push(draw).unwrap(); + } + >::insert(market_id, draws); + + >::set_block_number(aggregation_end + 1u64.saturated_into::()); + }: _(RawOrigin::Signed(caller), market_id) + + + punish_tardy_jurors { + let d in 1..T::MaxDraws::get(); + + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; + + let caller: T::AccountId = whitelisted_caller(); + let market_id = setup_court::()?; + + let mut court = >::get(market_id).unwrap(); + court.status = CourtStatus::Closed { + winner: OutcomeReport::Scalar(0u128), + punished: false, + reassigned: false, + }; + >::insert(market_id, court); + + fill_draws::(market_id, d)?; + }: _(RawOrigin::Signed(caller), market_id) + + reassign_juror_stakes { + let d in 1..T::MaxDraws::get(); + + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; + + let caller: T::AccountId = whitelisted_caller(); + let market_id = setup_court::()?; + + let mut court = >::get(market_id).unwrap(); + let winner_outcome = OutcomeReport::Scalar(0u128); + court.status = CourtStatus::Closed { + winner: winner_outcome.clone(), + punished: true, + reassigned: false, + }; + >::insert(market_id, court); + let salt = Default::default(); + // remove last random selections of on_dispute + >::remove(market_id); + let mut draws = >::get(market_id); + for i in 0..d { + let juror: T::AccountId = account("juror", i, 0); + >::insert(&juror, JurorInfo { + stake: T::MinJurorStake::get(), + active_lock: T::MinJurorStake::get(), + }); + let outcome = winner_outcome.clone(); + let secret = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let draw = + Draw { + juror, + vote: Vote::Revealed { secret, outcome, salt }, + weight: 1u32, + slashable: T::MinJurorStake::get(), + }; + draws.try_push(draw).unwrap(); + } + >::insert(market_id, draws); + }: _(RawOrigin::Signed(caller), market_id) impl_benchmark_test_suite!( Court, diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 15e5d914a..31601b154 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -121,6 +121,7 @@ mod pallet { type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, + Currency = Self::Currency, >; /// The maximum number of appeals until the court fails. @@ -603,7 +604,7 @@ mod pallet { ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); - let prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; + ensure!(>::contains_key(&juror), Error::::JurorDoesNotExist); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -641,12 +642,6 @@ mod pallet { debug_assert!(missing.is_zero(), "Could not slash all of the amount."); T::Currency::resolve_creating(&reward_pot, imbalance); - let mut jurors = JurorPool::::get(); - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake, &juror) { - jurors.remove(index); - >::put(jurors); - } - let raw_vote = Vote::Denounced { secret, outcome: outcome.clone(), salt }; draws[index] = Draw { juror: juror.clone(), vote: raw_vote, ..draw }; >::insert(market_id, draws); @@ -872,9 +867,8 @@ mod pallet { _ => return Err(Error::::CourtNotClosed.into()), }; - let mut jurors = JurorPool::::get(); let reward_pot = Self::reward_pot(&market_id); - let mut slash_and_remove_juror = |ai: &T::AccountId, slashable: BalanceOf| { + let slash_juror = |ai: &T::AccountId, slashable: BalanceOf| { let (imbalance, missing) = T::Currency::slash(ai, slashable); debug_assert!( missing.is_zero(), @@ -882,31 +876,15 @@ mod pallet { ai ); T::Currency::resolve_creating(&reward_pot, imbalance); - - if let Some(prev_juror_info) = >::get(ai) { - if let Some((index, _)) = - Self::get_pool_item(&jurors, prev_juror_info.stake, ai) - { - jurors.remove(index); - } - } else { - log::warn!( - "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ - {:?}.", - ai, - market_id - ); - debug_assert!(false); - } }; for draw in Draws::::get(market_id).iter() { match draw.vote { Vote::Drawn => { - slash_and_remove_juror(&draw.juror, draw.slashable); + slash_juror(&draw.juror, draw.slashable); } Vote::Secret { secret: _ } => { - slash_and_remove_juror(&draw.juror, draw.slashable); + slash_juror(&draw.juror, draw.slashable); } // denounce extrinsic already punished the juror Vote::Denounced { secret: _, outcome: _, salt: _ } => (), @@ -916,7 +894,6 @@ mod pallet { court.status = CourtStatus::Closed { winner, reassigned: false, punished: true }; >::insert(market_id, court); - >::put(jurors); Self::deposit_event(Event::TardyJurorsPunished { market_id }); @@ -1187,6 +1164,11 @@ mod pallet { rslt }); let mut seed = [0; 32]; + debug_assert!( + !>::block_number().is_zero(), + "When testing with the randomness of the collective flip pallet it produces a \ + underflow (block number substraction by one) panic if the block number is zero." + ); let (random_hash, _) = T::Random::random(&nonce.to_le_bytes()); seed.copy_from_slice(&random_hash.as_ref()[..32]); ChaCha20Rng::from_seed(seed) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 37dd4f295..596cbbe6d 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1276,6 +1276,25 @@ fn appeal_fails_if_only_global_dispute_appeal_allowed() { }); } +#[test] +fn appeal_last_works() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); + + fill_appeals(&market_id, (MaxAppeals::get() - 2) as usize); + + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + + assert_noop!( + Court::appeal(Origin::signed(CHARLIE), market_id), + Error::::OnlyGlobalDisputeAppealAllowed + ); + }); +} + #[test] fn check_appealable_market_fails_if_market_not_found() { ExtBuilder::default().build().execute_with(|| { diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 96b5e68a9..7948fd16f 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -1,4 +1,3 @@ -// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. @@ -19,11 +18,11 @@ //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-20, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-30, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/zeitgeist +// ./target/release/zeitgeist // benchmark // pallet // --chain=dev @@ -34,8 +33,8 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --template=./misc/weight_template.hbs // --output=./zrml/court/src/weights.rs +// --template=./misc/weight_template.hbs #![allow(unused_parens)] #![allow(unused_imports)] @@ -46,37 +45,149 @@ use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within /// zrml_court (automatically generated) pub trait WeightInfoZeitgeist { - fn exit_court() -> Weight; - fn join_court() -> Weight; - fn vote() -> Weight; + fn join_court(j: u32) -> Weight; + fn prepare_exit_court(j: u32) -> Weight; + fn exit_court_remove(j: u32) -> Weight; + fn exit_court_set(j: u32) -> Weight; + fn vote(d: u32) -> Weight; + fn denounce_vote(d: u32) -> Weight; + fn reveal_vote(d: u32) -> Weight; + fn appeal(d: u32, a: u32, r: u32) -> Weight; + fn back_global_dispute(d: u32, r: u32) -> Weight; + fn punish_tardy_jurors(d: u32) -> Weight; + fn reassign_juror_stakes(d: u32) -> Weight; } /// Weight functions for zrml_court (automatically generated) pub struct WeightInfo(PhantomData); impl WeightInfoZeitgeist for WeightInfo { + // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - // Storage: Court CounterForJurors (r:1 w:1) - // Storage: Court RequestedJurors (r:1 w:0) - // Storage: Court Votes (r:1 w:0) - fn exit_court() -> Weight { - Weight::from_ref_time(76_500_000) - .saturating_add(T::DbWeight::get().reads(5)) + // Storage: Balances Locks (r:1 w:1) + fn join_court(j: u32) -> Weight { + Weight::from_ref_time(46_917_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(56_000).saturating_mul(j.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } + // Storage: Court Jurors (r:1 w:0) + // Storage: Court JurorPool (r:1 w:1) + fn prepare_exit_court(j: u32) -> Weight { + Weight::from_ref_time(23_787_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(49_000).saturating_mul(j.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } // Storage: Court Jurors (r:1 w:1) - // Storage: Court CounterForJurors (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - fn join_court() -> Weight { - Weight::from_ref_time(54_370_000) + // Storage: Court JurorPool (r:1 w:0) + // Storage: Balances Locks (r:1 w:1) + fn exit_court_remove(j: u32) -> Weight { + Weight::from_ref_time(38_554_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(28_000).saturating_mul(j.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Court Jurors (r:1 w:1) + // Storage: Court JurorPool (r:1 w:0) + // Storage: Balances Locks (r:1 w:1) + fn exit_court_set(j: u32) -> Weight { + Weight::from_ref_time(37_829_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(28_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: Court Courts (r:1 w:0) + // Storage: Court Draws (r:1 w:1) + fn vote(d: u32) -> Weight { + Weight::from_ref_time(32_226_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(118_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Court Jurors (r:1 w:0) + // Storage: Court Courts (r:1 w:0) + // Storage: Court Draws (r:1 w:1) + // Storage: System Account (r:2 w:2) + fn denounce_vote(d: u32) -> Weight { + Weight::from_ref_time(62_581_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(140_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Court Jurors (r:1 w:0) - // Storage: Court Votes (r:0 w:1) - fn vote() -> Weight { - Weight::from_ref_time(25_540_000) - .saturating_add(T::DbWeight::get().reads(1)) + // Storage: Court Courts (r:1 w:0) + // Storage: Court Draws (r:1 w:1) + fn reveal_vote(d: u32) -> Weight { + Weight::from_ref_time(44_544_000) + // Standard Error: 13_000 + .saturating_add(Weight::from_ref_time(794_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } + // Storage: Court Courts (r:1 w:1) + // Storage: MarketCommons Markets (r:1 w:0) + // Storage: Court Draws (r:1 w:1) + // Storage: Court JurorPool (r:1 w:1) + // Storage: Court JurorsSelectionNonce (r:1 w:1) + // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + // Storage: Court Jurors (r:41 w:41) + // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) + // Storage: Court RequestBlock (r:1 w:0) + // Storage: Balances Reserves (r:1 w:1) + fn appeal(j: u32, a: u32, _r: u32) -> Weight { + Weight::from_ref_time(0) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(459_000).saturating_mul(j.into())) + // Standard Error: 374_000 + .saturating_add(Weight::from_ref_time(258_163_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(11)) + .saturating_add(T::DbWeight::get().reads((28_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(8)) + .saturating_add(T::DbWeight::get().writes((28_u64).saturating_mul(a.into()))) + } + // Storage: Court Courts (r:1 w:1) + // Storage: MarketCommons Markets (r:1 w:0) + // Storage: Court Draws (r:1 w:0) + // Storage: Balances Reserves (r:1 w:1) + // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) + fn back_global_dispute(d: u32, r: u32) -> Weight { + Weight::from_ref_time(56_987_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(182_000).saturating_mul(d.into())) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(25_000).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: Court Courts (r:1 w:1) + // Storage: Court Draws (r:1 w:0) + // Storage: System Account (r:2 w:2) + fn punish_tardy_jurors(d: u32) -> Weight { + Weight::from_ref_time(42_725_000) + // Standard Error: 4_000 + .saturating_add(Weight::from_ref_time(15_689_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) + } + // Storage: Court Courts (r:1 w:1) + // Storage: Court Draws (r:1 w:1) + // Storage: Court Jurors (r:1 w:1) + // Storage: System Account (r:1 w:0) + fn reassign_juror_stakes(d: u32) -> Weight { + Weight::from_ref_time(37_281_000) + // Standard Error: 2_000 + .saturating_add(Weight::from_ref_time(4_316_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) + } } From a8b625cece1806f7ad904db7b5cbabdc4eb7dd46 Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:46:44 +0200 Subject: [PATCH 169/368] Update zrml/court/src/types.rs Co-authored-by: Malte Kliemann --- zrml/court/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index a9435aec1..fcf9be935 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -96,7 +96,7 @@ pub enum CourtStatus { Open, /// The court case was closed, the winner outcome was determined. /// `punished` indicates whether the tardy jurors were punished. - /// `reassigned` indicates whether the winning jurors got the funds from the loosers already. + /// `reassigned` indicates if the winning jurors got the funds from the losers already. Closed { winner: OutcomeReport, punished: bool, reassigned: bool }, } From 5fcdb90905d30b62f294538eff6fb1093fc7d8eb Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:48:16 +0200 Subject: [PATCH 170/368] Update zrml/court/src/types.rs Co-authored-by: Malte Kliemann --- zrml/court/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index fcf9be935..775e0e63c 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -187,7 +187,7 @@ pub struct Draw { pub(crate) slashable: Balance, } -/// All information related one item in the stake weighted juror pool. +/// All information related to one item in the stake weighted juror pool. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, From 2a422d8d44829cbebe69b5d2d1c661d0d1578dd2 Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:48:55 +0200 Subject: [PATCH 171/368] Update zrml/court/src/lib.rs Co-authored-by: Malte Kliemann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 31601b154..934476517 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -323,7 +323,7 @@ mod pallet { MarketReportNotFound, /// The caller has not enough funds to join the court with the specified amount. InsufficientAmount, - /// After the first join of the court the amount has to be higher than the last join. + /// After the first join of the court the amount has to be higher than the current stake. AmountBelowLastJoin, /// The maximum number of normal appeals is reached. So only allow to back a global dispute. OnlyGlobalDisputeAppealAllowed, From 4ee444552de8e26becfe20c3d9de7ad7696e243a Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:49:18 +0200 Subject: [PATCH 172/368] Update zrml/court/src/lib.rs Co-authored-by: Malte Kliemann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 934476517..82a0301ee 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -445,7 +445,7 @@ mod pallet { } /// Prepare as a juror to exit the court. - /// For this the juror has to be removed from the stake weighted pool first before the exit. + /// For this the juror has to be removed from the stake-weighted pool first before the exit. /// Returns an error if the juror is already not part of the pool anymore. /// /// # Weight From 04090060f0b3e5dccac2135f8d6bd3854204eb5f Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:50:54 +0200 Subject: [PATCH 173/368] Update zrml/court/src/lib.rs Co-authored-by: Malte Kliemann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 82a0301ee..05c19300c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -155,7 +155,7 @@ mod pallet { type WeightInfo: WeightInfoZeitgeist; } - // Number of jurors for an initial market dispute + // Number of draws for an initial market dispute. const INITIAL_JURORS_NUM: usize = 5; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); From 6be9a23c3f983b563039833155446ae3d1bfca6e Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:51:29 +0200 Subject: [PATCH 174/368] Update zrml/court/src/mock_storage.rs Co-authored-by: Malte Kliemann --- zrml/court/src/mock_storage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/mock_storage.rs b/zrml/court/src/mock_storage.rs index c7908fa55..7c93b21b8 100644 --- a/zrml/court/src/mock_storage.rs +++ b/zrml/court/src/mock_storage.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Forecasting Technologies LTD. +// Copyright 2023 Forecasting Technologies LTD. // // This file is part of Zeitgeist. // From 44032bb3f8b96199d5db40106ad5cd56a796129f Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:52:14 +0200 Subject: [PATCH 175/368] Update zrml/court/src/lib.rs Co-authored-by: Malte Kliemann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 05c19300c..f6c22b6fb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -266,7 +266,7 @@ mod pallet { pub enum Error { /// An account id does not exist on the jurors storage. JurorDoesNotExist, - /// On dispute or resolution, someone tried to pass a non-court market type + /// On dispute or resolution, someone tried to pass a non-court market type. MarketDoesNotHaveCourtMechanism, /// The market is not in a state where it can be disputed. MarketIsNotDisputed, From 0cd22812b15b972d594526a183499914bee92255 Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:54:35 +0200 Subject: [PATCH 176/368] Update zrml/court/src/lib.rs Co-authored-by: Malte Kliemann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index f6c22b6fb..fa78697eb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -811,7 +811,7 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let appeal_number = court.appeals.len().saturating_add(1); - ensure!(appeal_number == >::bound(), Error::::NeedsToBeLastAppeal); + ensure!(appeal_number == T::MaxAppeals::get(), Error::::NeedsToBeLastAppeal); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; From c7962c539e572e4f6f4ad5f01211c35f87bbc688 Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 07:56:23 +0200 Subject: [PATCH 177/368] Update zrml/court/src/tests.rs Co-authored-by: Malte Kliemann --- zrml/court/src/tests.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 596cbbe6d..ffadb0184 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -391,8 +391,6 @@ fn prepare_exit_court_removes_correct_jurors() { assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - // println!("JurorPool: {:?}", JurorPool::::get().into_inner()); - for r in 0..max_accounts { let len = JurorPool::::get().into_inner().len(); assert!( From 5c62d094eb9d520da0e6bd6c4782344eefce462f Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 08:02:29 +0200 Subject: [PATCH 178/368] Update zrml/court/src/tests.rs Co-authored-by: Malte Kliemann --- zrml/court/src/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index ffadb0184..a4bbd8fbb 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -416,7 +416,6 @@ fn prepare_exit_court_fails_juror_already_prepared_to_exit() { ); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); - assert!(JurorPool::::get().into_inner().is_empty()); assert_noop!( Court::prepare_exit_court(Origin::signed(ALICE)), From ea3c10ad50373228eb37af456501d570109ea5a4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 08:32:57 +0200 Subject: [PATCH 179/368] periods to round timing, modify tests --- zrml/court/src/lib.rs | 31 +++++++++----------- zrml/court/src/tests.rs | 64 ----------------------------------------- zrml/court/src/types.rs | 28 +++++++++++------- 3 files changed, 32 insertions(+), 91 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index fa78697eb..a0e4dc6c5 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -43,7 +43,7 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, - JurorInfo, JurorPoolItem, Periods, Vote, + JurorInfo, JurorPoolItem, RoundTiming, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -766,15 +766,14 @@ mod pallet { let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); - let pre_vote_end = request_block.saturating_sub(now); - let periods = Periods { - pre_vote_end, - vote_end: T::CourtVotePeriod::get(), - aggregation_end: T::CourtAggregationPeriod::get(), - appeal_end: T::CourtAppealPeriod::get(), + let round_timing = RoundTiming { + pre_vote_end: request_block, + vote_period: T::CourtVotePeriod::get(), + aggregation_period: T::CourtAggregationPeriod::get(), + appeal_period: T::CourtAppealPeriod::get(), }; // sets periods one after the other from now - court.update_periods(periods, now); + court.update_periods(round_timing); let new_resolve_at = court.periods.appeal_end; debug_assert!(new_resolve_at != last_resolve_at); let _ids_len_1 = @@ -811,7 +810,7 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let appeal_number = court.appeals.len().saturating_add(1); - ensure!(appeal_number == T::MaxAppeals::get(), Error::::NeedsToBeLastAppeal); + ensure!(appeal_number as u32 == T::MaxAppeals::get(), Error::::NeedsToBeLastAppeal); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; @@ -1328,17 +1327,15 @@ mod pallet { let now = >::block_number(); let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); - let pre_vote_end = request_block.saturating_sub(now); - - let periods = Periods { - pre_vote_end, - vote_end: T::CourtVotePeriod::get(), - aggregation_end: T::CourtAggregationPeriod::get(), - appeal_end: T::CourtAppealPeriod::get(), + let round_timing = RoundTiming { + pre_vote_end: request_block, + vote_period: T::CourtVotePeriod::get(), + aggregation_period: T::CourtAggregationPeriod::get(), + appeal_period: T::CourtAppealPeriod::get(), }; // sets periods one after the other from now - let court = CourtInfo::new(now, periods); + let court = CourtInfo::new(round_timing); let _ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.periods.appeal_end)?; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a4bbd8fbb..b03940500 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -894,10 +894,6 @@ fn denounce_vote_works() { assert_eq!(old_draws[0].vote, Vote::Secret { secret }); assert_eq!(new_draws[0].vote, Vote::Denounced { secret, outcome, salt }); - >::get().iter().for_each(|pool_item| { - assert_ne!(pool_item.juror, ALICE); - }); - let free_alice_after = Balances::free_balance(ALICE); let slash = old_draws[0].slashable; assert!(!slash.is_zero()); @@ -1631,66 +1627,6 @@ fn punish_tardy_jurors_slashes_tardy_jurors_only() { }); } -#[test] -fn punish_tardy_jurors_removes_tardy_jurors_from_pool() { - ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); - let market_id = initialize_court(); - - let amount = MinJurorStake::get() * 100; - assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - assert_ok!(Court::join_court(Origin::signed(BOB), amount)); - assert_ok!(Court::join_court(Origin::signed(CHARLIE), amount)); - assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); - - let outcome = OutcomeReport::Scalar(42u128); - let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); - - let draws: crate::DrawsOf = vec![ - Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() }, - Draw { - juror: BOB, - weight: 1, - vote: Vote::Secret { secret }, - slashable: 2 * MinJurorStake::get(), - }, - Draw { - juror: CHARLIE, - weight: 1, - vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, - slashable: 3 * MinJurorStake::get(), - }, - Draw { - juror: DAVE, - weight: 1, - vote: Vote::Denounced { secret, outcome, salt }, - slashable: 4 * MinJurorStake::get(), - }, - ] - .try_into() - .unwrap(); - >::insert(market_id, draws); - - run_to_block(>::get() + 1); - - run_blocks( - CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, - ); - - let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::on_resolution(&market_id, &market).unwrap(); - - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - - let pool = >::get(); - assert!(!pool.clone().into_inner().iter().any(|pool_item| pool_item.juror == ALICE)); - assert!(!pool.clone().into_inner().iter().any(|pool_item| pool_item.juror == BOB)); - assert!(pool.clone().into_inner().iter().any(|pool_item| pool_item.juror == CHARLIE)); - assert!(pool.into_inner().iter().any(|pool_item| pool_item.juror == DAVE)); - }); -} - #[test] fn reassign_juror_stakes_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 775e0e63c..45c7f20ec 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -140,25 +140,33 @@ pub struct CourtInfo { pub(crate) periods: Periods, } +pub struct RoundTiming { + pub(crate) pre_vote_end: BlockNumber, + pub(crate) vote_period: BlockNumber, + pub(crate) aggregation_period: BlockNumber, + pub(crate) appeal_period: BlockNumber, +} + impl CourtInfo { - pub fn new(now: BlockNumber, periods: Periods) -> Self { - let pre_vote_end = now.saturating_add(periods.pre_vote_end); - let vote_end = pre_vote_end.saturating_add(periods.vote_end); - let aggregation_end = vote_end.saturating_add(periods.aggregation_end); - let appeal_end = aggregation_end.saturating_add(periods.appeal_end); + pub fn new(round_timing: RoundTiming) -> Self { + let pre_vote_end = round_timing.pre_vote_end; + let vote_end = pre_vote_end.saturating_add(round_timing.vote_period); + let aggregation_end = vote_end.saturating_add(round_timing.aggregation_period); + let appeal_end = aggregation_end.saturating_add(round_timing.appeal_period); let periods = Periods { pre_vote_end, vote_end, aggregation_end, appeal_end }; let status = CourtStatus::Open; Self { status, appeals: Default::default(), periods } } - pub fn update_periods(&mut self, periods: Periods, now: BlockNumber) { - self.periods.pre_vote_end = now.saturating_add(periods.pre_vote_end); - self.periods.vote_end = self.periods.pre_vote_end.saturating_add(periods.vote_end); + pub fn update_periods(&mut self, round_timing: RoundTiming) { + self.periods.pre_vote_end = round_timing.pre_vote_end; + self.periods.vote_end = self.periods.pre_vote_end.saturating_add(round_timing.vote_period); self.periods.aggregation_end = - self.periods.vote_end.saturating_add(periods.aggregation_end); - self.periods.appeal_end = self.periods.aggregation_end.saturating_add(periods.appeal_end); + self.periods.vote_end.saturating_add(round_timing.aggregation_period); + self.periods.appeal_end = + self.periods.aggregation_end.saturating_add(round_timing.appeal_period); } } From c787a64e8c1a40e2268ffa1f72b915fb0ac6ef1b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 08:46:22 +0200 Subject: [PATCH 180/368] modify doc comments --- zrml/court/src/types.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 45c7f20ec..56c4909cf 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -207,10 +207,9 @@ pub struct Draw { Eq, )] pub struct JurorPoolItem { - /// The key for the binary search to efficiently find the juror. - /// It has to be a unqiue key for each juror. + /// The amount of funds associated to a juror in order to get selected for a court case. pub(crate) stake: Balance, - /// The account which is the juror. + /// The account which is the juror that might be selected in court cases. pub(crate) juror: AccountId, /// The slashable amount for all random draws. This is useful to reduce the probability /// of a juror to be selected again. From fc3ec21e327faad7fa23999e7aa45d85a558e09f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 08:55:16 +0200 Subject: [PATCH 181/368] use onunbalanced --- zrml/court/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index a0e4dc6c5..fc799da47 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -56,7 +56,7 @@ mod pallet { pallet_prelude::{Hooks, OptionQuery, StorageMap, StorageValue, ValueQuery, Weight}, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, - NamedReservableCurrency, Randomness, ReservableCurrency, StorageVersion, + NamedReservableCurrency, OnUnbalanced, Randomness, ReservableCurrency, StorageVersion, WithdrawReasons, }, transactional, Blake2_128Concat, BoundedVec, PalletId, @@ -147,6 +147,9 @@ mod pallet { #[pallet::constant] type RequestInterval: Get; + /// Handler for slashed funds. + type Slash: OnUnbalanced>; + /// Slashed funds are send to the treasury #[pallet::constant] type TreasuryPalletId: Get; @@ -1224,8 +1227,7 @@ mod pallet { } } else { // if there are no winners reward the treasury - let treasury_acc = Self::treasury_account_id(); - T::Currency::resolve_creating(&treasury_acc, total_incentives); + T::Slash::on_unbalanced(total_incentives); } } From 8ec3a1df315bbbaa86ba80739dc980505433faba Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 09:06:27 +0200 Subject: [PATCH 182/368] improve RequestInterval documentation --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index fc799da47..4423dd841 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -143,7 +143,7 @@ mod pallet { /// Randomness source type Random: Randomness; - /// The interval for requesting multiple court votes at once. + /// The global interval which schedules the start of new court vote periods. #[pallet::constant] type RequestInterval: Get; From e5c93e55527510797e7b1fdfe17ce8fd1e1101fc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 09:08:31 +0200 Subject: [PATCH 183/368] improve consumed_stake documentation --- zrml/court/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 56c4909cf..4b76793d9 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -211,7 +211,7 @@ pub struct JurorPoolItem { pub(crate) stake: Balance, /// The account which is the juror that might be selected in court cases. pub(crate) juror: AccountId, - /// The slashable amount for all random draws. This is useful to reduce the probability + /// The consumed amount of the stake for all draws. This is useful to reduce the probability /// of a juror to be selected again. pub(crate) consumed_stake: Balance, } From c783f342562338ebfff5e1c73b9f6a5ef5c70e7e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 09:28:52 +0200 Subject: [PATCH 184/368] remove court from filter --- runtime/battery-station/src/lib.rs | 15 +++------------ runtime/zeitgeist/src/lib.rs | 14 ++++++-------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/runtime/battery-station/src/lib.rs b/runtime/battery-station/src/lib.rs index 7d74f2494..d9c6dcca2 100644 --- a/runtime/battery-station/src/lib.rs +++ b/runtime/battery-station/src/lib.rs @@ -101,29 +101,20 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { #[derive(scale_info::TypeInfo)] pub struct IsCallable; -// Currently disables Court, Rikiddo and creation of markets using Court. +// Currently disables Rikiddo. impl Contains for IsCallable { fn contains(call: &Call) -> bool { - use zeitgeist_primitives::types::{ - MarketDisputeMechanism::Court, ScoringRule::RikiddoSigmoidFeeMarketEma, - }; - use zrml_prediction_markets::Call::{ - create_cpmm_market_and_deploy_assets, create_market, edit_market, - }; + use zeitgeist_primitives::types::ScoringRule::RikiddoSigmoidFeeMarketEma; + use zrml_prediction_markets::Call::{create_market, edit_market}; #[allow(clippy::match_like_matches_macro)] match call { - Call::Court(_) => false, Call::LiquidityMining(_) => false, Call::PredictionMarkets(inner_call) => { match inner_call { // Disable Rikiddo markets create_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, edit_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, - // Disable Court dispute resolution mechanism - create_market { dispute_mechanism: Court, .. } => false, - create_cpmm_market_and_deploy_assets { dispute_mechanism: Court, .. } => false, - edit_market { dispute_mechanism: Court, .. } => false, _ => true, } } diff --git a/runtime/zeitgeist/src/lib.rs b/runtime/zeitgeist/src/lib.rs index a15182c26..e143564cb 100644 --- a/runtime/zeitgeist/src/lib.rs +++ b/runtime/zeitgeist/src/lib.rs @@ -100,7 +100,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { #[derive(scale_info::TypeInfo)] pub struct IsCallable; -// Currently disables Court, Rikiddo and creation of markets using Court or SimpleDisputes +// Currently disables Rikiddo and creation of markets using SimpleDisputes // dispute mechanism. impl Contains for IsCallable { fn contains(call: &Call) -> bool { @@ -115,8 +115,7 @@ impl Contains for IsCallable { use pallet_vesting::Call::force_vested_transfer; use zeitgeist_primitives::types::{ - MarketDisputeMechanism::{Court, SimpleDisputes}, - ScoringRule::RikiddoSigmoidFeeMarketEma, + MarketDisputeMechanism::SimpleDisputes, ScoringRule::RikiddoSigmoidFeeMarketEma, }; use zrml_prediction_markets::Call::{ create_cpmm_market_and_deploy_assets, create_market, edit_market, @@ -144,7 +143,6 @@ impl Contains for IsCallable { } // Membership is managed by the respective Membership instance Call::Council(set_members { .. }) => false, - Call::Court(_) => false, #[cfg(feature = "parachain")] Call::DmpQueue(service_overweight { .. }) => false, Call::LiquidityMining(_) => false, @@ -153,11 +151,11 @@ impl Contains for IsCallable { // Disable Rikiddo markets create_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, edit_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, - // Disable Court & SimpleDisputes dispute resolution mechanism - create_market { dispute_mechanism: Court | SimpleDisputes, .. } => false, - edit_market { dispute_mechanism: Court | SimpleDisputes, .. } => false, + // Disable SimpleDisputes dispute resolution mechanism + create_market { dispute_mechanism: SimpleDisputes, .. } => false, + edit_market { dispute_mechanism: SimpleDisputes, .. } => false, create_cpmm_market_and_deploy_assets { - dispute_mechanism: Court | SimpleDisputes, + dispute_mechanism: SimpleDisputes, .. } => false, _ => true, From 3f4e11a7f122cc32b681b9d5f14883d2b7a9e377 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 09:51:24 +0200 Subject: [PATCH 185/368] add treasury to court --- Cargo.lock | 1 + zrml/court/Cargo.toml | 1 + zrml/court/src/mock.rs | 33 ++++++++++++++++++++++++++--- zrml/prediction-markets/src/mock.rs | 1 + 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17b38e1de..e363a283b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12916,6 +12916,7 @@ dependencies = [ "pallet-balances", "pallet-randomness-collective-flip", "pallet-timestamp", + "pallet-treasury", "parity-scale-codec", "rand 0.8.5", "rand_chacha 0.3.1", diff --git a/zrml/court/Cargo.toml b/zrml/court/Cargo.toml index 7b70d988a..1b22cd3ae 100644 --- a/zrml/court/Cargo.toml +++ b/zrml/court/Cargo.toml @@ -15,6 +15,7 @@ zrml-market-commons = { default-features = false, path = "../market-commons" } pallet-balances = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } pallet-randomness-collective-flip = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } pallet-timestamp = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } +pallet-treasury = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } sp-io = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } zeitgeist-primitives = { default-features = false, features = ["mock"], path = "../../primitives" } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 823a62672..224b9ccff 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -20,12 +20,13 @@ use crate::{self as zrml_court, mock_storage::pallet as mock_storage}; use frame_support::{ - construct_runtime, + construct_runtime, ord_parameter_types, pallet_prelude::{DispatchError, Weight}, parameter_types, - traits::{Everything, Hooks}, + traits::{Everything, Hooks, NeverEnsureOrigin}, PalletId, }; +use frame_system::EnsureSignedBy; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, @@ -33,7 +34,7 @@ use sp_runtime::{ use zeitgeist_primitives::{ constants::mock::{ AppealBond, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, - CourtPalletId, CourtVotePeriod, MaxAppeals, MaxDraws, MaxJurors, MaxReserves, + CourtPalletId, CourtVotePeriod, MaxAppeals, MaxApprovals, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RequestInterval, BASE, }, traits::DisputeResolutionApi, @@ -54,6 +55,11 @@ pub const HARRY: AccountIdTest = 7; pub const IAN: AccountIdTest = 8; pub const POOR_PAUL: AccountIdTest = 9; pub const INITIAL_BALANCE: u128 = 1000 * BASE; +pub const SUDO: AccountIdTest = 69; + +ord_parameter_types! { + pub const Sudo: AccountIdTest = SUDO; +} parameter_types! { pub const TreasuryPalletId: PalletId = PalletId(*b"3.141592"); @@ -72,6 +78,7 @@ construct_runtime!( RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, System: frame_system::{Call, Config, Event, Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet}, + Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, // Just a mock storage for testing. MockStorage: mock_storage::{Storage}, } @@ -147,6 +154,7 @@ impl crate::Config for Runtime { type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RequestInterval = RequestInterval; + type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = crate::weights::WeightInfo; } @@ -206,6 +214,25 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } +impl pallet_treasury::Config for Runtime { + type ApproveOrigin = EnsureSignedBy; + type Burn = (); + type BurnDestination = (); + type Currency = Balances; + type Event = Event; + type MaxApprovals = MaxApprovals; + type OnSlash = (); + type PalletId = TreasuryPalletId; + type ProposalBond = (); + type ProposalBondMinimum = (); + type ProposalBondMaximum = (); + type RejectOrigin = EnsureSignedBy; + type SpendFunds = (); + type SpendOrigin = NeverEnsureOrigin; + type SpendPeriod = (); + type WeightInfo = (); +} + pub struct ExtBuilder { balances: Vec<(AccountIdTest, Balance)>, } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 9a5c236a9..fb4640a22 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -294,6 +294,7 @@ impl zrml_court::Config for Runtime { type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RequestInterval = RequestInterval; + type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } From 7ba93092ab6193fc4f48cfea29b304d20ae6978f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 10:21:57 +0200 Subject: [PATCH 186/368] correct errors --- zrml/court/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 4423dd841..3dd81c6cb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -325,7 +325,7 @@ mod pallet { /// The report of the market was not found. MarketReportNotFound, /// The caller has not enough funds to join the court with the specified amount. - InsufficientAmount, + AmountExceedsBalance, /// After the first join of the court the amount has to be higher than the current stake. AmountBelowLastJoin, /// The maximum number of normal appeals is reached. So only allow to back a global dispute. @@ -340,6 +340,8 @@ mod pallet { JurorTwiceInPool, /// The caller of this function is not part of the juror draws. CallerNotInDraws, + /// The callers balance is lower than the appeal bond. + AppealBondExceedsBalance, } #[pallet::hooks] @@ -376,7 +378,7 @@ mod pallet { let who = ensure_signed(origin)?; ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); let free_balance = T::Currency::free_balance(&who); - ensure!(amount <= free_balance, Error::::InsufficientAmount); + ensure!(amount <= free_balance, Error::::AmountExceedsBalance); let mut jurors = JurorPool::::get(); @@ -744,7 +746,7 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let appeal_number = court.appeals.len().saturating_add(1); let bond = default_appeal_bond::(appeal_number); - ensure!(T::Currency::can_reserve(&who, bond), Error::::InsufficientAmount); + ensure!(T::Currency::can_reserve(&who, bond), Error::::AppealBondExceedsBalance); ensure!( appeal_number < >::bound(), Error::::OnlyGlobalDisputeAppealAllowed From 0e0e8adeb32b3af33e08a19ada9a5d5f5bac399b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 10:23:42 +0200 Subject: [PATCH 187/368] update comment --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 3dd81c6cb..b51336a5e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1104,7 +1104,7 @@ mod pallet { Ok(new_draws) } - // Returns (index, pool_item) if the stake associated with the juror was found. + // Returns (index, pool_item) if the pool item is part of the juror pool. // It returns None otherwise. pub(crate) fn get_pool_item<'a>( jurors: &'a [JurorPoolItemOf], From 6eb35ba29a40649ad47222e8975f493a244913fb Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 10:24:42 +0200 Subject: [PATCH 188/368] update get_pool_item comment --- zrml/court/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b51336a5e..f0b03cc90 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1116,9 +1116,8 @@ mod pallet { }) { return Some((i, &jurors[i])); } - // this None case can happen when the lowest bonded juror was removed (`join_court`) - // it can also happen when juror was denounced, - // did not vote or reveal or when the juror decided to leave the court + // this None case can happen whenever the juror decided to leave the court + // or was kicked out of the juror pool because of the lowest stake None } From 33500216d02fc8820e0db937cdd8d41ca860e341 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 10:25:16 +0200 Subject: [PATCH 189/368] update get_pool_item comment --- zrml/court/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b51336a5e..f0b03cc90 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1116,9 +1116,8 @@ mod pallet { }) { return Some((i, &jurors[i])); } - // this None case can happen when the lowest bonded juror was removed (`join_court`) - // it can also happen when juror was denounced, - // did not vote or reveal or when the juror decided to leave the court + // this None case can happen whenever the juror decided to leave the court + // or was kicked out of the juror pool because of the lowest stake None } From b4a45a74850b6327e0efb1840443f8f8299d0eb7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 10:27:20 +0200 Subject: [PATCH 190/368] comment juror pool --- zrml/court/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index f0b03cc90..1823021a1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -199,6 +199,7 @@ mod pallet { pub struct Pallet(PhantomData); /// The pool of jurors who can get randomly selected according to their stake. + /// The pool is sorted by stake in ascending order [min, ..., max]. #[pallet::storage] pub type JurorPool = StorageValue<_, JurorPoolOf, ValueQuery>; From 6fe0223858918e02f303b6fabfa480673e003812 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 10:35:49 +0200 Subject: [PATCH 191/368] improve doc comments --- zrml/court/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 1823021a1..8703c57b3 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -451,7 +451,9 @@ mod pallet { } /// Prepare as a juror to exit the court. - /// For this the juror has to be removed from the stake-weighted pool first before the exit. + /// When this is called the juror is not anymore able to get drawn for new cases. + /// The juror gets removed from the stake-weighted pool. + /// After that the juror can exit the court. /// Returns an error if the juror is already not part of the pool anymore. /// /// # Weight @@ -471,8 +473,7 @@ mod pallet { >::put(jurors); } else { // this error can happen if the lowest bonded juror was removed - // it can also happen when juror was denounced, - // did not vote or reveal + // or if the current extrinsic was already called before return Err(Error::::JurorAlreadyPreparedToExit.into()); } @@ -480,7 +481,10 @@ mod pallet { Ok(()) } - /// Remove the juror stake from resolved courts. + /// Exit the court. + /// The stake which is not locked by any court case is unlocked. + /// `prepare_exit_court` must be called before + /// to remove the juror from the stake-weighted pool. /// /// # Arguments /// From d9a3b9eb816d9236c2d4e2482230a3243e13e65c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 10:36:00 +0200 Subject: [PATCH 192/368] cargo fmt --- zrml/court/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 8703c57b3..072023b16 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -452,7 +452,7 @@ mod pallet { /// Prepare as a juror to exit the court. /// When this is called the juror is not anymore able to get drawn for new cases. - /// The juror gets removed from the stake-weighted pool. + /// The juror gets removed from the stake-weighted pool. /// After that the juror can exit the court. /// Returns an error if the juror is already not part of the pool anymore. /// @@ -483,7 +483,7 @@ mod pallet { /// Exit the court. /// The stake which is not locked by any court case is unlocked. - /// `prepare_exit_court` must be called before + /// `prepare_exit_court` must be called before /// to remove the juror from the stake-weighted pool. /// /// # Arguments @@ -1121,7 +1121,7 @@ mod pallet { }) { return Some((i, &jurors[i])); } - // this None case can happen whenever the juror decided to leave the court + // this None case can happen whenever the juror decided to leave the court // or was kicked out of the juror pool because of the lowest stake None } From 06af2fbfeac6fe208b8ae9ce408ab77a8480172d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 11:00:53 +0200 Subject: [PATCH 193/368] rename constants --- zrml/court/src/lib.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 072023b16..0a1c1e7a2 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -159,12 +159,12 @@ mod pallet { } // Number of draws for an initial market dispute. - const INITIAL_JURORS_NUM: usize = 5; + const INITIAL_DRAWS_NUM: usize = 5; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); // Weight used to increase the number of jurors for subsequent appeals // of the same market - const SUBSEQUENT_JURORS_FACTOR: usize = 2; + const APPEAL_BASIS: usize = 2; // Basis used to increase the bond for subsequent appeals of the same market const APPEAL_BOND_BASIS: u32 = 2; @@ -1182,16 +1182,14 @@ mod pallet { ChaCha20Rng::from_seed(seed) } - // Calculates the necessary number of jurors depending on the number of market appeals. + // Calculates the necessary number of draws depending on the number of market appeals. pub(crate) fn necessary_jurors_weight(appeals_len: usize) -> usize { // 2^(appeals_len) * 5 + 2^(appeals_len) - 1 - // MaxAppeals - 1 (= 5) example: 2^5 * 5 + 2^5 - 1 = 191 - SUBSEQUENT_JURORS_FACTOR + // MaxAppeals - 1 (= 3) example: 2^3 * 5 + 2^3 - 1 = 47 + APPEAL_BASIS .saturating_pow(appeals_len as u32) - .saturating_mul(INITIAL_JURORS_NUM) - .saturating_add( - SUBSEQUENT_JURORS_FACTOR.saturating_pow(appeals_len as u32).saturating_sub(1), - ) + .saturating_mul(INITIAL_DRAWS_NUM) + .saturating_add(APPEAL_BASIS.saturating_pow(appeals_len as u32).saturating_sub(1)) } fn slash_losers_to_award_winners( From 3c0de154a607a0a92c2f28779fdc912ef81ad1af Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 11:02:55 +0200 Subject: [PATCH 194/368] edit court pallet doc --- zrml/court/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 0a1c1e7a2..9385dd7cb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -16,10 +16,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -// It is important to note that if a categorical market has only two outcomes, then winners -// won't receive any rewards because accounts of the most voted outcome on the loser side are -// simply registered as `JurorStatus::Tardy`. - #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::type_complexity)] From bb61066c8ded703cb8b63de734ff18f173e0de47 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 11:08:28 +0200 Subject: [PATCH 195/368] update tests --- zrml/court/src/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index b03940500..fe0fd6221 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -291,13 +291,13 @@ fn join_court_fails_below_min_juror_stake() { } #[test] -fn join_court_fails_insufficient_amount() { +fn join_court_fails_if_amount_exceeds_balance() { ExtBuilder::default().build().execute_with(|| { let min = MinJurorStake::get(); let amount = min + 1; assert_noop!( Court::join_court(Origin::signed(POOR_PAUL), amount), - Error::::InsufficientAmount + Error::::AmountExceedsBalance ); }); } @@ -1238,7 +1238,7 @@ fn appeal_fails_if_court_not_found() { } #[test] -fn appeal_fails_if_insufficient_amount() { +fn appeal_fails_if_appeal_bond_exceeds_balance() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); @@ -1247,7 +1247,7 @@ fn appeal_fails_if_insufficient_amount() { assert_noop!( Court::appeal(Origin::signed(POOR_PAUL), market_id), - Error::::InsufficientAmount + Error::::AppealBondExceedsBalance ); }); } From 4729e1b46a2453ee2b3c9641754d5a4eb7977299 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 11:18:54 +0200 Subject: [PATCH 196/368] rename secret to commitment --- runtime/common/src/lib.rs | 1 + zrml/court/src/benchmarks.rs | 24 ++++---- zrml/court/src/lib.rs | 82 ++++++++++++++------------- zrml/court/src/tests.rs | 105 ++++++++++++++++++----------------- zrml/court/src/types.rs | 6 +- 5 files changed, 114 insertions(+), 104 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index dde77a862..9702d36bf 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -963,6 +963,7 @@ macro_rules! impl_config_traits { type MinJurorStake = MinJurorStake; type Random = RandomnessCollectiveFlip; type RequestInterval = RequestInterval; + type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 70b7bf3ee..3102df2c2 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -242,8 +242,8 @@ benchmarks! { >::set_block_number(pre_vote_end + 1u64.saturated_into::()); - let secret_vote = Default::default(); - }: _(RawOrigin::Signed(caller), market_id, secret_vote) + let commitment_vote = Default::default(); + }: _(RawOrigin::Signed(caller), market_id, commitment_vote) denounce_vote { let d in 1..T::MaxDraws::get(); @@ -268,13 +268,13 @@ benchmarks! { active_lock: T::MinJurorStake::get(), }); let denounced_juror_unlookup = T::Lookup::unlookup(denounced_juror.clone()); - let secret = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); let mut draws = >::get(market_id); let draws_len = draws.len(); draws[draws_len.saturating_sub(1usize)] = Draw { juror: denounced_juror, - vote: Vote::Secret { secret }, + vote: Vote::Secret { commitment }, weight: 1u32, slashable: T::MinJurorStake::get(), }; @@ -303,13 +303,13 @@ benchmarks! { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), }); - let secret = T::Hashing::hash_of(&(caller.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(caller.clone(), outcome.clone(), salt)); let mut draws = >::get(market_id); let draws_len = draws.len(); draws[draws_len.saturating_sub(1usize)] = Draw { juror: caller.clone(), - vote: Vote::Secret { secret }, + vote: Vote::Secret { commitment }, weight: 1u32, slashable: T::MinJurorStake::get(), }; @@ -364,11 +364,11 @@ benchmarks! { active_lock: T::MinJurorStake::get(), }); let outcome = OutcomeReport::Scalar(i as u128); - let secret = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); let draw = Draw { juror, - vote: Vote::Revealed { secret, outcome, salt }, + vote: Vote::Revealed { commitment, outcome, salt }, weight: 1u32, slashable: >::zero() }; @@ -436,11 +436,11 @@ benchmarks! { active_lock: T::MinJurorStake::get(), }); let outcome = OutcomeReport::Scalar(i as u128); - let secret = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); let draw = Draw { juror, - vote: Vote::Revealed { secret, outcome, salt }, + vote: Vote::Revealed { commitment, outcome, salt }, weight: 1u32, slashable: T::MinJurorStake::get(), }; @@ -501,11 +501,11 @@ benchmarks! { active_lock: T::MinJurorStake::get(), }); let outcome = winner_outcome.clone(); - let secret = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); let draw = Draw { juror, - vote: Vote::Revealed { secret, outcome, salt }, + vote: Vote::Revealed { commitment, outcome, salt }, weight: 1u32, slashable: T::MinJurorStake::get(), }; diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9385dd7cb..9405fad52 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -77,11 +77,11 @@ mod pallet { #[pallet::constant] type AppealBond: Get>; - /// The time in which the jurors can cast their secret vote. + /// The time in which the jurors can cast their commitment vote. #[pallet::constant] type CourtVotePeriod: Get; - /// The time in which the jurors should reveal their secret vote. + /// The time in which the jurors should reveal their commitment vote. #[pallet::constant] type CourtAggregationPeriod: Get; @@ -236,7 +236,7 @@ mod pallet { /// A juror has been removed from the court. JurorExited { juror: T::AccountId, exit_amount: BalanceOf, active_lock: BalanceOf }, /// A juror has voted in a court. - JurorVoted { market_id: MarketIdOf, juror: T::AccountId, secret: T::Hash }, + JurorVoted { market_id: MarketIdOf, juror: T::AccountId, commitment: T::Hash }, /// A juror has revealed their vote. JurorRevealedVote { juror: T::AccountId, @@ -272,9 +272,9 @@ mod pallet { MarketIsNotDisputed, /// Only jurors can reveal their votes. OnlyJurorsCanReveal, - /// The vote is not secret. + /// The vote is not commitment. VoteAlreadyRevealed, - /// The outcome and salt reveal do not match the secret vote. + /// The outcome and salt reveal do not match the commitment vote. InvalidReveal, /// No court for this market id was found. CourtNotFound, @@ -288,7 +288,7 @@ mod pallet { NotInAppealPeriod, /// The court is already present for this market. CourtAlreadyExists, - /// The caller of this extrinsic needs to be drawn or in the secret vote state. + /// The caller of this extrinsic needs to be drawn or in the commitment vote state. InvalidVoteState, /// The amount is below the minimum required stake. BelowMinJurorStake, @@ -303,7 +303,7 @@ mod pallet { JurorNeedsToExit, /// The juror was not randomly selected for the court. JurorNotDrawn, - /// The juror was drawn but did not manage to secretly vote within the court. + /// The juror was drawn but did not manage to commitmently vote within the court. JurorNotVoted, /// The juror was already denounced. This action can only happen once. VoteAlreadyDenounced, @@ -534,7 +534,7 @@ mod pallet { /// # Arguments /// /// - `market_id`: The identifier of the court. - /// - `secret_vote`: A hash which consists of `juror ++ outcome ++ salt`. + /// - `commitment_vote`: A hash which consists of `juror ++ outcome ++ salt`. /// /// # Weight /// @@ -545,7 +545,7 @@ mod pallet { pub fn vote( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, - secret_vote: T::Hash, + commitment_vote: T::Hash, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -561,7 +561,7 @@ mod pallet { Some(index) => { // allow to override last vote ensure!( - matches!(draws[index].vote, Vote::Drawn | Vote::Secret { secret: _ }), + matches!(draws[index].vote, Vote::Drawn | Vote::Secret { commitment: _ }), Error::::InvalidVoteState ); (index, draws[index].clone()) @@ -569,25 +569,29 @@ mod pallet { None => return Err(Error::::CallerNotInDraws.into()), }; - let vote = Vote::Secret { secret: secret_vote }; + let vote = Vote::Secret { commitment: commitment_vote }; draws[index] = Draw { juror: who.clone(), vote, ..draw }; >::insert(market_id, draws); - Self::deposit_event(Event::JurorVoted { juror: who, market_id, secret: secret_vote }); + Self::deposit_event(Event::JurorVoted { + juror: who, + market_id, + commitment: commitment_vote, + }); Ok(()) } - /// Denounce a juror during the voting period for which the secret vote is known. + /// Denounce a juror during the voting period for which the commitment vote is known. /// This is useful to punish the behaviour that jurors reveal - /// their secrets before the voting period ends. - /// A check of `secret_hash == hash(juror ++ outcome ++ salt)` is performed for validation. + /// their commitments before the voting period ends. + /// A check of `commitment_hash == hash(juror ++ outcome ++ salt)` is performed for validation. /// /// # Arguments /// /// - `market_id`: The identifier of the court. - /// - `juror`: The juror whose secret vote might be known. - /// - `outcome`: The raw vote outcome which should match with the secret of the juror. + /// - `juror`: The juror whose commitment vote might be known. + /// - `outcome`: The raw vote outcome which should match with the commitment of the juror. /// - `salt`: The hash which is used to proof that the juror did reveal /// her vote during the voting period. /// @@ -626,19 +630,19 @@ mod pallet { None => return Err(Error::::JurorNotDrawn.into()), }; - let secret = match draw.vote { - Vote::Secret { secret } => { + let commitment = match draw.vote { + Vote::Secret { commitment } => { ensure!( - secret == T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)), + commitment == T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)), Error::::InvalidReveal ); - secret + commitment } Vote::Drawn => return Err(Error::::JurorNotVoted.into()), - Vote::Revealed { secret: _, outcome: _, salt: _ } => { + Vote::Revealed { commitment: _, outcome: _, salt: _ } => { return Err(Error::::VoteAlreadyRevealed.into()); } - Vote::Denounced { secret: _, outcome: _, salt: _ } => { + Vote::Denounced { commitment: _, outcome: _, salt: _ } => { return Err(Error::::VoteAlreadyDenounced.into()); } }; @@ -648,7 +652,7 @@ mod pallet { debug_assert!(missing.is_zero(), "Could not slash all of the amount."); T::Currency::resolve_creating(&reward_pot, imbalance); - let raw_vote = Vote::Denounced { secret, outcome: outcome.clone(), salt }; + let raw_vote = Vote::Denounced { commitment, outcome: outcome.clone(), salt }; draws[index] = Draw { juror: juror.clone(), vote: raw_vote, ..draw }; >::insert(market_id, draws); @@ -662,13 +666,13 @@ mod pallet { Ok(()) } - /// Reveal the secret vote of the caller juror. - /// A check of `secret_hash == hash(juror ++ outcome ++ salt)` is performed for validation. + /// Reveal the commitment vote of the caller juror. + /// A check of `commitment_hash == hash(juror ++ outcome ++ salt)` is performed for validation. /// /// # Arguments /// /// - `market_id`: The identifier of the court. - /// - `outcome`: The raw vote outcome which should match with the secret of the juror. + /// - `outcome`: The raw vote outcome which should match with the commitment of the juror. /// - `salt`: The hash which is used for the validation. /// /// # Weight @@ -699,29 +703,29 @@ mod pallet { None => return Err(Error::::JurorNotDrawn.into()), }; - let secret = match draw.vote { - Vote::Secret { secret } => { + let commitment = match draw.vote { + Vote::Secret { commitment } => { // market id and current appeal number is part of salt generation // salt should be signed by the juror (market_id ++ appeal number) // salt can be reproduced only be the juror address // with knowing market_id and appeal number // so even if the salt is forgotten it can be reproduced only by the juror ensure!( - secret == T::Hashing::hash_of(&(who.clone(), outcome.clone(), salt)), + commitment == T::Hashing::hash_of(&(who.clone(), outcome.clone(), salt)), Error::::InvalidReveal ); - secret + commitment } Vote::Drawn => return Err(Error::::JurorNotVoted.into()), - Vote::Revealed { secret: _, outcome: _, salt: _ } => { + Vote::Revealed { commitment: _, outcome: _, salt: _ } => { return Err(Error::::VoteAlreadyRevealed.into()); } - Vote::Denounced { secret: _, outcome: _, salt: _ } => { + Vote::Denounced { commitment: _, outcome: _, salt: _ } => { return Err(Error::::VoteAlreadyDenounced.into()); } }; - let raw_vote = Vote::Revealed { secret, outcome: outcome.clone(), salt }; + let raw_vote = Vote::Revealed { commitment, outcome: outcome.clone(), salt }; draws[index] = Draw { juror: who.clone(), vote: raw_vote, ..draw }; >::insert(market_id, draws); @@ -888,12 +892,12 @@ mod pallet { Vote::Drawn => { slash_juror(&draw.juror, draw.slashable); } - Vote::Secret { secret: _ } => { + Vote::Secret { commitment: _ } => { slash_juror(&draw.juror, draw.slashable); } // denounce extrinsic already punished the juror - Vote::Denounced { secret: _, outcome: _, salt: _ } => (), - Vote::Revealed { secret: _, outcome: _, salt: _ } => (), + Vote::Denounced { commitment: _, outcome: _, salt: _ } => (), + Vote::Revealed { commitment: _, outcome: _, salt: _ } => (), } } @@ -950,7 +954,7 @@ mod pallet { ); debug_assert!(false); } - if let Vote::Revealed { secret: _, outcome, salt: _ } = draw.vote { + if let Vote::Revealed { commitment: _, outcome, salt: _ } = draw.vote { valid_winners_and_losers.push((draw.juror, outcome, draw.slashable)); } } @@ -1241,7 +1245,7 @@ mod pallet { let mut scores = BTreeMap::::new(); for draw in draws { - if let Vote::Revealed { secret: _, outcome, salt: _ } = &draw.vote { + if let Vote::Revealed { commitment: _, outcome, salt: _ } = &draw.vote { if let Some(el) = scores.get_mut(outcome) { *el = el.saturating_add(draw.weight); } else { diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index fe0fd6221..c6cb64ac3 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -147,10 +147,10 @@ fn set_alice_after_vote( run_to_block(>::get() + 1); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); - (market_id, secret, salt) + (market_id, commitment, salt) } const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ @@ -531,9 +531,9 @@ fn vote_works() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); - System::assert_last_event(Event::JurorVoted { juror: ALICE, market_id, secret }.into()); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); + System::assert_last_event(Event::JurorVoted { juror: ALICE, market_id, commitment }.into()); let new_draws = >::get(market_id); assert_eq!(old_draws[1..], new_draws[1..]); @@ -541,7 +541,7 @@ fn vote_works() { assert_eq!(old_draws[0].weight, new_draws[0].weight); assert_eq!(old_draws[0].slashable, new_draws[0].slashable); assert_eq!(old_draws[0].vote, Vote::Drawn); - assert_eq!(new_draws[0].vote, Vote::Secret { secret }); + assert_eq!(new_draws[0].vote, Vote::Secret { commitment }); }); } @@ -560,14 +560,14 @@ fn vote_overwrite_works() { let wrong_outcome = OutcomeReport::Scalar(69u128); let salt = ::Hash::default(); - let wrong_secret = BlakeTwo256::hash_of(&(ALICE, wrong_outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, wrong_secret)); + let wrong_commitment = BlakeTwo256::hash_of(&(ALICE, wrong_outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, wrong_commitment)); run_blocks(1); let right_outcome = OutcomeReport::Scalar(42u128); - let new_secret = BlakeTwo256::hash_of(&(ALICE, right_outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, new_secret)); + let new_commitment = BlakeTwo256::hash_of(&(ALICE, right_outcome, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, new_commitment)); }); } @@ -575,9 +575,9 @@ fn vote_overwrite_works() { fn vote_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { let market_id = 0; - let secret = ::Hash::default(); + let commitment = ::Hash::default(); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, secret), + Court::vote(Origin::signed(ALICE), market_id, commitment), Error::::CourtNotFound ); }); @@ -585,14 +585,14 @@ fn vote_fails_if_court_not_found() { #[test_case( Vote::Revealed { - secret: ::Hash::default(), + commitment: ::Hash::default(), outcome: OutcomeReport::Scalar(1u128), salt: ::Hash::default(), }; "revealed" )] #[test_case( Vote::Denounced { - secret: ::Hash::default(), + commitment: ::Hash::default(), outcome: OutcomeReport::Scalar(1u128), salt: ::Hash::default(), }; "denounced" @@ -614,9 +614,9 @@ fn vote_fails_if_vote_state_incorrect(vote: crate::Vote<::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, secret), + Court::vote(Origin::signed(ALICE), market_id, commitment), Error::::InvalidVoteState ); }); @@ -636,9 +636,9 @@ fn vote_fails_if_caller_not_in_draws() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, secret), + Court::vote(Origin::signed(ALICE), market_id, commitment), Error::::CallerNotInDraws ); }); @@ -659,9 +659,9 @@ fn vote_fails_if_not_in_voting_period() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, secret), + Court::vote(Origin::signed(ALICE), market_id, commitment), Error::::NotInVotingPeriod ); }); @@ -682,8 +682,8 @@ fn reveal_vote_works() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); let old_draws = >::get(market_id); @@ -700,8 +700,8 @@ fn reveal_vote_works() { assert_eq!(old_draws[0].juror, new_draws[0].juror); assert_eq!(old_draws[0].weight, new_draws[0].weight); assert_eq!(old_draws[0].slashable, new_draws[0].slashable); - assert_eq!(old_draws[0].vote, Vote::Secret { secret }); - assert_eq!(new_draws[0].vote, Vote::Revealed { secret, outcome, salt }); + assert_eq!(old_draws[0].vote, Vote::Secret { commitment }); + assert_eq!(new_draws[0].vote, Vote::Revealed { commitment, outcome, salt }); }); } @@ -855,7 +855,7 @@ fn reveal_vote_fails_if_already_denounced() { fn denounce_vote_works() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, secret, salt) = set_alice_after_vote(outcome.clone()); + let (market_id, commitment, salt) = set_alice_after_vote(outcome.clone()); let old_draws = >::get(market_id); assert!( @@ -891,8 +891,8 @@ fn denounce_vote_works() { assert_eq!(old_draws[0].juror, new_draws[0].juror); assert_eq!(old_draws[0].weight, new_draws[0].weight); assert_eq!(old_draws[0].slashable, new_draws[0].slashable); - assert_eq!(old_draws[0].vote, Vote::Secret { secret }); - assert_eq!(new_draws[0].vote, Vote::Denounced { secret, outcome, salt }); + assert_eq!(old_draws[0].vote, Vote::Secret { commitment }); + assert_eq!(new_draws[0].vote, Vote::Denounced { commitment, outcome, salt }); let free_alice_after = Balances::free_balance(ALICE); let slash = old_draws[0].slashable; @@ -1204,8 +1204,8 @@ fn appeal_get_latest_resolved_outcome_changes() { run_to_block(request_block + 1); let outcome = OutcomeReport::Scalar(69u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, secret)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); run_blocks(CourtVotePeriod::get() + 1); @@ -1561,27 +1561,27 @@ fn punish_tardy_jurors_slashes_tardy_jurors_only() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); let draws: crate::DrawsOf = vec![ Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() }, Draw { juror: BOB, weight: 1, - vote: Vote::Secret { secret }, + vote: Vote::Secret { commitment }, slashable: 2 * MinJurorStake::get(), }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, slashable: 3 * MinJurorStake::get(), }, Draw { juror: DAVE, weight: 1, vote: Vote::Drawn, slashable: 4 * MinJurorStake::get() }, Draw { juror: EVE, weight: 1, - vote: Vote::Denounced { secret, outcome, salt }, + vote: Vote::Denounced { commitment, outcome, salt }, slashable: 5 * MinJurorStake::get(), }, ] @@ -1770,7 +1770,7 @@ fn reassign_juror_stakes_decreases_active_lock() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); let alice_slashable = MinJurorStake::get(); >::mutate(ALICE, |juror_info| { @@ -1799,17 +1799,22 @@ fn reassign_juror_stakes_decreases_active_lock() { let draws: crate::DrawsOf = vec![ Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: alice_slashable }, - Draw { juror: BOB, weight: 1, vote: Vote::Secret { secret }, slashable: bob_slashable }, + Draw { + juror: BOB, + weight: 1, + vote: Vote::Secret { commitment }, + slashable: bob_slashable, + }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, slashable: charlie_slashable, }, Draw { juror: DAVE, weight: 1, - vote: Vote::Denounced { secret, outcome, salt }, + vote: Vote::Denounced { commitment, outcome, salt }, slashable: dave_slashable, }, ] @@ -1850,7 +1855,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); let wrong_outcome_0 = OutcomeReport::Scalar(69u128); let wrong_outcome_1 = OutcomeReport::Scalar(56u128); @@ -1859,25 +1864,25 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, slashable: MinJurorStake::get(), }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { secret, outcome: wrong_outcome_0, salt }, + vote: Vote::Revealed { commitment, outcome: wrong_outcome_0, salt }, slashable: 2 * MinJurorStake::get(), }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { secret, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, slashable: 3 * MinJurorStake::get(), }, Draw { juror: DAVE, weight: 1, - vote: Vote::Revealed { secret, outcome: wrong_outcome_1, salt }, + vote: Vote::Revealed { commitment, outcome: wrong_outcome_1, salt }, slashable: 4 * MinJurorStake::get(), }, ] @@ -1942,7 +1947,7 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); - let secret = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); let wrong_outcome_0 = OutcomeReport::Scalar(69u128); let wrong_outcome_1 = OutcomeReport::Scalar(56u128); @@ -1951,25 +1956,25 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { secret, outcome: wrong_outcome_1.clone(), salt }, + vote: Vote::Revealed { commitment, outcome: wrong_outcome_1.clone(), salt }, slashable: MinJurorStake::get(), }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { secret, outcome: wrong_outcome_0.clone(), salt }, + vote: Vote::Revealed { commitment, outcome: wrong_outcome_0.clone(), salt }, slashable: 2 * MinJurorStake::get(), }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { secret, outcome: wrong_outcome_0, salt }, + vote: Vote::Revealed { commitment, outcome: wrong_outcome_0, salt }, slashable: 3 * MinJurorStake::get(), }, Draw { juror: DAVE, weight: 1, - vote: Vote::Revealed { secret, outcome: wrong_outcome_1, salt }, + vote: Vote::Revealed { commitment, outcome: wrong_outcome_1, salt }, slashable: 4 * MinJurorStake::get(), }, ] @@ -2210,12 +2215,12 @@ fn prepare_draws(market_id: &crate::MarketIdOf, outcomes_with_weights: let juror = offset_i as u128; let salt = BlakeTwo256::hash_of(&offset_i); let outcome = OutcomeReport::Scalar(*outcome_index); - let secret = BlakeTwo256::hash_of(&(juror, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(juror, outcome.clone(), salt)); draws .try_push(Draw { juror, weight: *weight, - vote: Vote::Revealed { secret, outcome, salt }, + vote: Vote::Revealed { commitment, outcome, salt }, slashable: 0u128, }) .unwrap(); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 4b76793d9..75ff3ef05 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -51,11 +51,11 @@ pub enum Vote { /// The juror was randomly selected to vote in a specific court case. Drawn, /// The juror casted a vote, only providing a hash, which meaning is unknown. - Secret { secret: Hash }, + Secret { commitment: Hash }, /// The juror revealed her raw vote, letting anyone know what she voted. - Revealed { secret: Hash, outcome: OutcomeReport, salt: Hash }, + Revealed { commitment: Hash, outcome: OutcomeReport, salt: Hash }, /// The juror was denounced, because she revealed her raw vote during the vote phase. - Denounced { secret: Hash, outcome: OutcomeReport, salt: Hash }, + Denounced { commitment: Hash, outcome: OutcomeReport, salt: Hash }, } /// The information about the lifecycle of a court case. From 7cbdbe5a55b7ec886f2537ff515506b36c069cec Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 11:47:51 +0200 Subject: [PATCH 197/368] make commit reveal failsafer --- zrml/court/src/lib.rs | 48 ++++++++++++++++++++++++++++------------- zrml/court/src/types.rs | 7 ++++++ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9405fad52..b476ddfaf 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -38,8 +38,8 @@ pub use types::*; #[frame_support::pallet] mod pallet { use crate::{ - weights::WeightInfoZeitgeist, AppealInfo, CourtInfo, CourtPalletApi, CourtStatus, Draw, - JurorInfo, JurorPoolItem, RoundTiming, Vote, + weights::WeightInfoZeitgeist, AppealInfo, CommitmentMatcher, CourtInfo, CourtPalletApi, + CourtStatus, Draw, JurorInfo, JurorPoolItem, RoundTiming, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -189,6 +189,8 @@ mod pallet { pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; + pub(crate) type CommitmentMatcherOf = + CommitmentMatcher, ::Hash>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -632,10 +634,13 @@ mod pallet { let commitment = match draw.vote { Vote::Secret { commitment } => { - ensure!( - commitment == T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)), - Error::::InvalidReveal - ); + let commitment_matcher = CommitmentMatcher { + commitment, + juror: juror.clone(), + outcome: outcome.clone(), + salt, + }; + Self::is_valid(commitment_matcher)?; commitment } Vote::Drawn => return Err(Error::::JurorNotVoted.into()), @@ -705,15 +710,13 @@ mod pallet { let commitment = match draw.vote { Vote::Secret { commitment } => { - // market id and current appeal number is part of salt generation - // salt should be signed by the juror (market_id ++ appeal number) - // salt can be reproduced only be the juror address - // with knowing market_id and appeal number - // so even if the salt is forgotten it can be reproduced only by the juror - ensure!( - commitment == T::Hashing::hash_of(&(who.clone(), outcome.clone(), salt)), - Error::::InvalidReveal - ); + let commitment_matcher = CommitmentMatcher { + commitment, + juror: who.clone(), + outcome: outcome.clone(), + salt, + }; + Self::is_valid(commitment_matcher)?; commitment } Vote::Drawn => return Err(Error::::JurorNotVoted.into()), @@ -1301,6 +1304,21 @@ mod pallet { Self::get_winner(last_draws, last_winner).unwrap_or(oracle_outcome); Ok(resolved_outcome) } + + pub(crate) fn is_valid(commitment_matcher: CommitmentMatcherOf) -> DispatchResult { + // market id and current appeal number is part of salt generation + // salt should be signed by the juror (market_id ++ appeal number) + // salt can be reproduced only be the juror address + // with knowing market_id and appeal number + // so even if the salt is forgotten it can be reproduced only by the juror + let CommitmentMatcher { commitment, juror, outcome, salt } = commitment_matcher; + ensure!( + commitment == T::Hashing::hash_of(&(juror, outcome, salt)), + Error::::InvalidReveal + ); + + Ok(()) + } } impl DisputeApi for Pallet diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 75ff3ef05..076e4cf83 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -36,6 +36,13 @@ pub struct JurorInfo { pub(crate) active_lock: Balance, } +pub struct CommitmentMatcher { + pub(crate) commitment: Hash, + pub(crate) juror: AccountId, + pub(crate) outcome: OutcomeReport, + pub(crate) salt: Hash, +} + /// All possible states of a vote. #[derive( parity_scale_codec::Decode, From 1c732de3dcd978bf31f39c064a54e2ae03990659 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 12:10:26 +0200 Subject: [PATCH 198/368] outsource commitment function --- zrml/court/src/lib.rs | 77 ++++++++++++++++++++--------------------- zrml/court/src/types.rs | 8 +++-- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b476ddfaf..97c480407 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -39,7 +39,7 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CommitmentMatcher, CourtInfo, CourtPalletApi, - CourtStatus, Draw, JurorInfo, JurorPoolItem, RoundTiming, Vote, + CourtStatus, Draw, JurorInfo, JurorPoolItem, RawCommitment, RoundTiming, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -191,6 +191,8 @@ mod pallet { pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; pub(crate) type CommitmentMatcherOf = CommitmentMatcher, ::Hash>; + pub(crate) type RawCommitmentOf = + RawCommitment, ::Hash>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -632,25 +634,10 @@ mod pallet { None => return Err(Error::::JurorNotDrawn.into()), }; - let commitment = match draw.vote { - Vote::Secret { commitment } => { - let commitment_matcher = CommitmentMatcher { - commitment, - juror: juror.clone(), - outcome: outcome.clone(), - salt, - }; - Self::is_valid(commitment_matcher)?; - commitment - } - Vote::Drawn => return Err(Error::::JurorNotVoted.into()), - Vote::Revealed { commitment: _, outcome: _, salt: _ } => { - return Err(Error::::VoteAlreadyRevealed.into()); - } - Vote::Denounced { commitment: _, outcome: _, salt: _ } => { - return Err(Error::::VoteAlreadyDenounced.into()); - } - }; + let raw_commmitment = + RawCommitment { juror: juror.clone(), outcome: outcome.clone(), salt }; + + let commitment = Self::get_hashed_commitment(draw.vote, raw_commmitment)?; let reward_pot = Self::reward_pot(&market_id); let (imbalance, missing) = T::Currency::slash(&juror, draw.slashable); @@ -708,25 +695,10 @@ mod pallet { None => return Err(Error::::JurorNotDrawn.into()), }; - let commitment = match draw.vote { - Vote::Secret { commitment } => { - let commitment_matcher = CommitmentMatcher { - commitment, - juror: who.clone(), - outcome: outcome.clone(), - salt, - }; - Self::is_valid(commitment_matcher)?; - commitment - } - Vote::Drawn => return Err(Error::::JurorNotVoted.into()), - Vote::Revealed { commitment: _, outcome: _, salt: _ } => { - return Err(Error::::VoteAlreadyRevealed.into()); - } - Vote::Denounced { commitment: _, outcome: _, salt: _ } => { - return Err(Error::::VoteAlreadyDenounced.into()); - } - }; + let raw_commitment = + RawCommitment { juror: who.clone(), outcome: outcome.clone(), salt }; + + let commitment = Self::get_hashed_commitment(draw.vote, raw_commitment)?; let raw_vote = Vote::Revealed { commitment, outcome: outcome.clone(), salt }; draws[index] = Draw { juror: who.clone(), vote: raw_vote, ..draw }; @@ -1311,7 +1283,11 @@ mod pallet { // salt can be reproduced only be the juror address // with knowing market_id and appeal number // so even if the salt is forgotten it can be reproduced only by the juror - let CommitmentMatcher { commitment, juror, outcome, salt } = commitment_matcher; + let CommitmentMatcher { + hashed: commitment, + raw: RawCommitment { juror, outcome, salt }, + } = commitment_matcher; + ensure!( commitment == T::Hashing::hash_of(&(juror, outcome, salt)), Error::::InvalidReveal @@ -1319,6 +1295,27 @@ mod pallet { Ok(()) } + + pub(crate) fn get_hashed_commitment( + vote: Vote, + raw_commitment: RawCommitmentOf, + ) -> Result { + match vote { + Vote::Secret { commitment } => { + let commitment_matcher = + CommitmentMatcher { hashed: commitment, raw: raw_commitment }; + Self::is_valid(commitment_matcher)?; + Ok(commitment) + } + Vote::Drawn => Err(Error::::JurorNotVoted.into()), + Vote::Revealed { commitment: _, outcome: _, salt: _ } => { + Err(Error::::VoteAlreadyRevealed.into()) + } + Vote::Denounced { commitment: _, outcome: _, salt: _ } => { + Err(Error::::VoteAlreadyDenounced.into()) + } + } + } } impl DisputeApi for Pallet diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 076e4cf83..4212c49e6 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -36,13 +36,17 @@ pub struct JurorInfo { pub(crate) active_lock: Balance, } -pub struct CommitmentMatcher { - pub(crate) commitment: Hash, +pub struct RawCommitment { pub(crate) juror: AccountId, pub(crate) outcome: OutcomeReport, pub(crate) salt: Hash, } +pub struct CommitmentMatcher { + pub(crate) hashed: Hash, + pub(crate) raw: RawCommitment, +} + /// All possible states of a vote. #[derive( parity_scale_codec::Decode, From 06c0152f52155d33fdb393c76867edd0abf77e97 Mon Sep 17 00:00:00 2001 From: Chralt Date: Fri, 31 Mar 2023 12:13:36 +0200 Subject: [PATCH 199/368] Update scripts/tests/misc.sh --- scripts/tests/misc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/misc.sh b/scripts/tests/misc.sh index 85055edea..9431faf60 100755 --- a/scripts/tests/misc.sh +++ b/scripts/tests/misc.sh @@ -18,7 +18,7 @@ test_package_with_feature primitives std $rustflags no_runtime_benchmarks=('court' 'market-commons' 'rikiddo') -cargo +nightly-2022-09-24 test --package zeitgeist-runtime --lib -- --nocapture +cargo test --package zeitgeist-runtime --lib -- --nocapture # TODO(#848): Delete when feature "with-global-dispute" is removed cargo test -p zrml-prediction-markets --features with-global-disputes,parachain From c7e3986a4150642d4698fd4777b3e2099e4e7449 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 12:38:08 +0200 Subject: [PATCH 200/368] update doc comments --- zrml/court/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 97c480407..8bfd29d02 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -825,7 +825,8 @@ mod pallet { Ok(()) } - /// After the court is closed (resolution happened), the tardy jurors can get punished. + /// Punish all tardy jurors for the given court associated to the market id. + /// This needs to be called before `reassign_juror_stakes`. /// /// # Arguments /// From 5302bd3e8e6ef7becc5eff6837328fc975daa770 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 13:31:22 +0200 Subject: [PATCH 201/368] merge punish tardy jurors reassign juror stakes --- zrml/court/src/benchmarks.rs | 27 +----- zrml/court/src/lib.rs | 99 ++++++---------------- zrml/court/src/tests.rs | 154 ++++------------------------------- zrml/court/src/types.rs | 6 +- 4 files changed, 42 insertions(+), 244 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 3102df2c2..563ee936d 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -451,27 +451,6 @@ benchmarks! { >::set_block_number(aggregation_end + 1u64.saturated_into::()); }: _(RawOrigin::Signed(caller), market_id) - - punish_tardy_jurors { - let d in 1..T::MaxDraws::get(); - - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; - - let caller: T::AccountId = whitelisted_caller(); - let market_id = setup_court::()?; - - let mut court = >::get(market_id).unwrap(); - court.status = CourtStatus::Closed { - winner: OutcomeReport::Scalar(0u128), - punished: false, - reassigned: false, - }; - >::insert(market_id, court); - - fill_draws::(market_id, d)?; - }: _(RawOrigin::Signed(caller), market_id) - reassign_juror_stakes { let d in 1..T::MaxDraws::get(); @@ -483,11 +462,7 @@ benchmarks! { let mut court = >::get(market_id).unwrap(); let winner_outcome = OutcomeReport::Scalar(0u128); - court.status = CourtStatus::Closed { - winner: winner_outcome.clone(), - punished: true, - reassigned: false, - }; + court.status = CourtStatus::Closed { winner: winner_outcome.clone() }; >::insert(market_id, court); let salt = Default::default(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 8bfd29d02..35892dedf 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -260,8 +260,6 @@ mod pallet { MarketAppealed { market_id: MarketIdOf, appeal_number: u32 }, /// The juror stakes have been reassigned. JurorStakesReassigned { market_id: MarketIdOf }, - /// The tardy jurors have been punished. - TardyJurorsPunished { market_id: MarketIdOf }, /// The global dispute backing was successful. GlobalDisputeBacked { market_id: MarketIdOf }, } @@ -315,12 +313,8 @@ mod pallet { SelfDenounceDisallowed, /// The court is not in the closed state. CourtNotClosed, - /// The juror stakes were already reassigned. - JurorStakesAlreadyReassigned, - /// The tardy jurors were already punished. - TardyJurorsAlreadyPunished, - /// Punish the tardy jurors first. - PunishTardyJurorsFirst, + /// The juror stakes of the court already got reassigned. + CourtAlreadyReassigned, /// There are not enough jurors in the pool. NotEnoughJurors, /// The report of the market was not found. @@ -825,8 +819,7 @@ mod pallet { Ok(()) } - /// Punish all tardy jurors for the given court associated to the market id. - /// This needs to be called before `reassign_juror_stakes`. + /// After the court is closed (resolution happened) the juror stakes can get reassigned. /// /// # Arguments /// @@ -837,7 +830,7 @@ mod pallet { /// Complexity: `O(n)`, where `n` is the number of randomly selected jurors for this court. #[pallet::weight(1_000_000_000_000)] #[transactional] - pub fn punish_tardy_jurors( + pub fn reassign_juror_stakes( origin: OriginFor, market_id: MarketIdOf, ) -> DispatchResult { @@ -845,13 +838,13 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { - CourtStatus::Closed { winner, punished, reassigned: _ } => { - ensure!(!punished, Error::::TardyJurorsAlreadyPunished); - winner - } + CourtStatus::Closed { winner } => winner, + CourtStatus::Reassigned => return Err(Error::::CourtAlreadyReassigned.into()), _ => return Err(Error::::CourtNotClosed.into()), }; + let draws = Draws::::get(market_id); + let reward_pot = Self::reward_pot(&market_id); let slash_juror = |ai: &T::AccountId, slashable: BalanceOf| { let (imbalance, missing) = T::Currency::slash(ai, slashable); @@ -863,58 +856,6 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); }; - for draw in Draws::::get(market_id).iter() { - match draw.vote { - Vote::Drawn => { - slash_juror(&draw.juror, draw.slashable); - } - Vote::Secret { commitment: _ } => { - slash_juror(&draw.juror, draw.slashable); - } - // denounce extrinsic already punished the juror - Vote::Denounced { commitment: _, outcome: _, salt: _ } => (), - Vote::Revealed { commitment: _, outcome: _, salt: _ } => (), - } - } - - court.status = CourtStatus::Closed { winner, reassigned: false, punished: true }; - >::insert(market_id, court); - - Self::deposit_event(Event::TardyJurorsPunished { market_id }); - - Ok(()) - } - - /// After the court is closed (resolution happened) and the tardy jurors have been punished, - /// the juror stakes can get reassigned. - /// - /// # Arguments - /// - /// - `market_id`: The identifier of the court. - /// - /// # Weight - /// - /// Complexity: `O(n)`, where `n` is the number of randomly selected jurors for this court. - #[pallet::weight(1_000_000_000_000)] - #[transactional] - pub fn reassign_juror_stakes( - origin: OriginFor, - market_id: MarketIdOf, - ) -> DispatchResult { - ensure_signed(origin)?; - - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let winner = match court.status { - CourtStatus::Closed { winner, punished, reassigned } => { - ensure!(!reassigned, Error::::JurorStakesAlreadyReassigned); - ensure!(punished, Error::::PunishTardyJurorsFirst); - winner - } - _ => return Err(Error::::CourtNotClosed.into()), - }; - - let draws = Draws::::get(market_id); - let mut valid_winners_and_losers = Vec::with_capacity(draws.len()); for draw in draws { @@ -930,8 +871,19 @@ mod pallet { ); debug_assert!(false); } - if let Vote::Revealed { commitment: _, outcome, salt: _ } = draw.vote { - valid_winners_and_losers.push((draw.juror, outcome, draw.slashable)); + + match draw.vote { + Vote::Drawn => { + slash_juror(&draw.juror, draw.slashable); + } + Vote::Secret { commitment: _ } => { + slash_juror(&draw.juror, draw.slashable); + } + // denounce extrinsic already punished the juror + Vote::Denounced { commitment: _, outcome: _, salt: _ } => (), + Vote::Revealed { commitment: _, outcome, salt: _ } => { + valid_winners_and_losers.push((draw.juror, outcome, draw.slashable)); + } } } @@ -941,8 +893,9 @@ mod pallet { &winner, ); - court.status = CourtStatus::Closed { winner, punished: true, reassigned: true }; + court.status = CourtStatus::Reassigned; >::insert(market_id, court); + >::remove(market_id); Self::deposit_event(Event::JurorStakesReassigned { market_id }); @@ -1380,11 +1333,7 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let draws = Draws::::get(market_id); let resolved_outcome = Self::get_latest_resolved_outcome(market_id, draws.as_slice())?; - court.status = CourtStatus::Closed { - winner: resolved_outcome.clone(), - punished: false, - reassigned: false, - }; + court.status = CourtStatus::Closed { winner: resolved_outcome.clone() }; >::insert(market_id, court); Ok(Some(resolved_outcome)) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index c6cb64ac3..101b3d5e3 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1451,103 +1451,7 @@ fn back_global_dispute_adds_last_appeal() { } #[test] -fn punish_tardy_jurors_fails_if_court_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Court::punish_tardy_jurors(Origin::signed(CHARLIE), 0), - Error::::CourtNotFound - ); - }); -} - -#[test] -fn punish_tardy_jurors_fails_if_court_not_closed() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - assert_noop!( - Court::punish_tardy_jurors(Origin::signed(CHARLIE), market_id), - Error::::CourtNotClosed - ); - }); -} - -#[test] -fn punish_tardy_jurors_fails_if_tardy_jurors_already_punished() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::on_resolution(&market_id, &market).unwrap(); - - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - - assert_noop!( - Court::punish_tardy_jurors(Origin::signed(CHARLIE), market_id), - Error::::TardyJurorsAlreadyPunished - ); - }); -} - -#[test] -fn punish_tardy_jurors_emits_event() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::on_resolution(&market_id, &market).unwrap(); - - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - - System::assert_last_event(Event::TardyJurorsPunished { market_id }.into()); - }); -} - -#[test] -fn punish_tardy_jurors_updates_court_status() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - let court = >::get(market_id).unwrap(); - assert_eq!(court.status, CourtStatus::Open); - - let market = MarketCommons::market(&market_id).unwrap(); - let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().unwrap(); - - let court = >::get(market_id).unwrap(); - assert_eq!( - court.status, - CourtStatus::Closed { - winner: resolution_outcome.clone(), - reassigned: false, - punished: false - } - ); - - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - - let court = >::get(market_id).unwrap(); - assert_eq!( - court.status, - CourtStatus::Closed { winner: resolution_outcome, reassigned: false, punished: true } - ); - }); -} - -#[test] -fn punish_tardy_jurors_slashes_tardy_jurors_only() { +fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); let market_id = initialize_court(); @@ -1605,7 +1509,7 @@ fn punish_tardy_jurors_slashes_tardy_jurors_only() { let free_dave_before = Balances::free_balance(&DAVE); let free_eve_before = Balances::free_balance(&EVE); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); let free_alice_after = Balances::free_balance(&ALICE); assert_ne!(free_alice_after, free_alice_before); @@ -1616,7 +1520,13 @@ fn punish_tardy_jurors_slashes_tardy_jurors_only() { assert_eq!(free_bob_after, free_bob_before - old_draws[BOB as usize].slashable); let free_charlie_after = Balances::free_balance(&CHARLIE); - assert_eq!(free_charlie_after, free_charlie_before); + assert_eq!( + free_charlie_after, + free_charlie_before + + old_draws[ALICE as usize].slashable + + old_draws[BOB as usize].slashable + + old_draws[DAVE as usize].slashable + ); let free_dave_after = Balances::free_balance(&DAVE); assert_ne!(free_dave_after, free_dave_before); @@ -1637,19 +1547,6 @@ fn reassign_juror_stakes_fails_if_court_not_found() { }); } -#[test] -fn reassign_juror_stakes_fails_if_tardy_jurors_not_punished() { - ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - let market = MarketCommons::market(&market_id).unwrap(); - let _ = Court::on_resolution(&market_id, &market).unwrap(); - assert_noop!( - Court::reassign_juror_stakes(Origin::signed(EVE), market_id), - Error::::PunishTardyJurorsFirst - ); - }); -} - #[test] fn reassign_juror_stakes_emits_event() { ExtBuilder::default().build().execute_with(|| { @@ -1661,8 +1558,6 @@ fn reassign_juror_stakes_emits_event() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().unwrap(); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); System::assert_last_event(Event::JurorStakesReassigned { market_id }.into()); }); @@ -1679,12 +1574,11 @@ fn reassign_juror_stakes_fails_if_juror_stakes_already_reassigned() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().unwrap(); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_noop!( Court::reassign_juror_stakes(Origin::signed(EVE), market_id), - Error::::JurorStakesAlreadyReassigned + Error::::CourtAlreadyReassigned ); }); } @@ -1700,25 +1594,13 @@ fn reassign_juror_stakes_updates_court_status() { let market = MarketCommons::market(&market_id).unwrap(); let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().unwrap(); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - let court = >::get(market_id).unwrap(); - assert_eq!( - court.status, - CourtStatus::Closed { - winner: resolution_outcome.clone(), - punished: true, - reassigned: false - } - ); + assert_eq!(court.status, CourtStatus::Closed { winner: resolution_outcome }); assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); let court = >::get(market_id).unwrap(); - assert_eq!( - court.status, - CourtStatus::Closed { winner: resolution_outcome, punished: true, reassigned: true } - ); + assert_eq!(court.status, CourtStatus::Reassigned); }); } @@ -1733,8 +1615,6 @@ fn reassign_juror_stakes_removes_draws() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().unwrap(); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - let draws = >::get(market_id); assert!(!draws.is_empty()); @@ -1831,8 +1711,6 @@ fn reassign_juror_stakes_decreases_active_lock() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap(); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); assert!(>::get(ALICE).unwrap().active_lock.is_zero()); assert!(>::get(BOB).unwrap().active_lock.is_zero()); @@ -1901,8 +1779,6 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().unwrap(); assert_eq!(resolution_outcome, outcome); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - let free_alice_before = Balances::free_balance(ALICE); let free_bob_before = Balances::free_balance(BOB); let free_charlie_before = Balances::free_balance(CHARLIE); @@ -1990,11 +1866,9 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { ); let mut court = >::get(market_id).unwrap(); - court.status = CourtStatus::Closed { winner: outcome, punished: false, reassigned: false }; + court.status = CourtStatus::Closed { winner: outcome }; >::insert(market_id, court); - assert_ok!(Court::punish_tardy_jurors(Origin::signed(EVE), market_id)); - let free_alice_before = Balances::free_balance(ALICE); let free_bob_before = Balances::free_balance(BOB); let free_charlie_before = Balances::free_balance(CHARLIE); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 4212c49e6..59678e082 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -106,9 +106,9 @@ pub enum CourtStatus { /// The court case has been started. Open, /// The court case was closed, the winner outcome was determined. - /// `punished` indicates whether the tardy jurors were punished. - /// `reassigned` indicates if the winning jurors got the funds from the losers already. - Closed { winner: OutcomeReport, punished: bool, reassigned: bool }, + Closed { winner: OutcomeReport }, + /// The juror stakes from the court were reassigned + Reassigned, } /// The information about an appeal for a court case. From 0bd260a4d3d67723a4b7687f8982244b0bd0a55d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 14:00:49 +0200 Subject: [PATCH 202/368] outsource get_n_random_numbers --- zrml/court/src/lib.rs | 49 ++++++++++++++++++++++++----------------- zrml/court/src/tests.rs | 12 ++++------ 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 35892dedf..c94e35e19 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -908,26 +908,17 @@ mod pallet { where T: Config, { - pub(crate) fn choose_multiple_weighted( - jurors: &mut JurorPoolOf, - number: usize, - rng: &mut R, - ) -> Result>, DispatchError> { - let total_weight = jurors - .iter() - .map(|pool_item| { - pool_item - .stake - .saturating_sub(pool_item.consumed_stake) - .saturated_into::() - }) - .sum::(); + pub(crate) fn get_n_random_numbers( + n: usize, + max: u128, + ) -> Result, DispatchError> { + let mut rng = Self::rng(); let mut random_set = BTreeSet::new(); let mut insert_unused_random_number = || -> DispatchResult { let mut count = 0u8; // this loop is to make sure we don't insert the same random number twice - while !random_set.insert(rng.gen_range(0u128..=total_weight)) { + while !random_set.insert(rng.gen_range(0u128..=max)) { count = count.saturating_add(1u8); if count >= 3u8 { return Err(Error::::RandNumGenFailed.into()); @@ -936,10 +927,30 @@ mod pallet { Ok(()) }; - for _ in 0..number { + for _ in 0..n { insert_unused_random_number()?; } - debug_assert!(random_set.len() == number); + + debug_assert!(random_set.len() == n); + + Ok(random_set) + } + + pub(crate) fn choose_multiple_weighted( + jurors: &mut JurorPoolOf, + number: usize, + ) -> Result>, DispatchError> { + let total_weight = jurors + .iter() + .map(|pool_item| { + pool_item + .stake + .saturating_sub(pool_item.consumed_stake) + .saturated_into::() + }) + .sum::(); + + let mut random_set = Self::get_n_random_numbers(number, total_weight)?; let mut selections = BTreeMap::)>::new(); @@ -1004,10 +1015,8 @@ mod pallet { let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); ensure!(jurors.len() >= necessary_jurors_weight, Error::::NotEnoughJurors); - let mut rng = Self::rng(); - let random_jurors = - Self::choose_multiple_weighted(&mut jurors, necessary_jurors_weight, &mut rng)?; + Self::choose_multiple_weighted(&mut jurors, necessary_jurors_weight)?; debug_assert!( random_jurors.len() <= T::MaxDraws::get() as usize, diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 101b3d5e3..3840760f1 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1930,7 +1930,6 @@ fn on_resolution_denies_non_court_markets() { fn choose_multiple_weighted_works() { ExtBuilder::default().build().execute_with(|| { let necessary_jurors_weight = Court::necessary_jurors_weight(5usize); - let mut rng = Court::rng(); for i in 0..necessary_jurors_weight { let amount = MinJurorStake::get() + i as u128; let juror = i as u128; @@ -1939,8 +1938,7 @@ fn choose_multiple_weighted_works() { } let mut jurors = JurorPool::::get(); let random_jurors = - Court::choose_multiple_weighted(&mut jurors, necessary_jurors_weight, &mut rng) - .unwrap(); + Court::choose_multiple_weighted(&mut jurors, necessary_jurors_weight).unwrap(); assert_eq!( random_jurors.iter().map(|draw| draw.weight).sum::() as usize, necessary_jurors_weight @@ -2187,15 +2185,14 @@ fn random_jurors_returns_an_unique_different_subset_of_jurors() { jurors.try_push(pool_item.clone()).unwrap(); } - let mut rng = Court::rng(); - let random_jurors = Court::choose_multiple_weighted(&mut jurors, 3, &mut rng).unwrap(); + let random_jurors = Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); let mut at_least_one_set_is_different = false; for _ in 0..100 { run_blocks(1); let another_set_of_random_jurors = - Court::choose_multiple_weighted(&mut jurors, 3, &mut rng).unwrap(); + Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); let mut iter = another_set_of_random_jurors.iter(); if let Some(juror) = iter.next() { @@ -2229,8 +2226,7 @@ fn random_jurors_returns_a_subset_of_jurors() { jurors.try_push(pool_item.clone()).unwrap(); } - let mut rng = Court::rng(); - let random_jurors = Court::choose_multiple_weighted(&mut jurors, 2, &mut rng).unwrap(); + let random_jurors = Court::choose_multiple_weighted(&mut jurors, 2).unwrap(); for draw in random_jurors { assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.juror == draw.juror)); } From c2c06834db905cbcf2f0f9430bb5a7fba4b8e935 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 14:06:44 +0200 Subject: [PATCH 203/368] correct logs --- zrml/court/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index c94e35e19..1870e1185 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -864,8 +864,8 @@ mod pallet { >::insert(&draw.juror, juror_info); } else { log::warn!( - "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ - {:?}.", + "Juror {:?} not found in Jurors storage (reassign_juror_stakes). Market \ + id {:?}.", draw.juror, market_id ); @@ -1031,8 +1031,7 @@ mod pallet { >::insert(&old_draw.juror, juror_info); } else { log::warn!( - "Juror {:?} not found in Jurors storage for vote aggregation. Market id \ - {:?}.", + "Juror {:?} not found in Jurors storage (select_jurors). Market id {:?}.", old_draw.juror, market_id ); From b75d015d105707a5070bfe672914126a2fed4802 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 31 Mar 2023 14:12:10 +0200 Subject: [PATCH 204/368] rename default to get --- zrml/court/src/benchmarks.rs | 4 ++-- zrml/court/src/lib.rs | 6 +++--- zrml/court/src/tests.rs | 22 +++++++++++----------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 563ee936d..263814e48 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -345,7 +345,7 @@ benchmarks! { for i in 0..a { let appeal_info = AppealInfo { backer: account("backer", i, 0), - bond: crate::default_appeal_bond::(i as usize), + bond: crate::get_appeal_bond::(i as usize), appealed_outcome: OutcomeReport::Scalar(0u128), }; court.appeals.try_push(appeal_info).unwrap(); @@ -418,7 +418,7 @@ benchmarks! { for i in 0..(T::MaxAppeals::get() - 1) { let appeal_info = AppealInfo { backer: account("backer", i, 0), - bond: crate::default_appeal_bond::(i as usize), + bond: crate::get_appeal_bond::(i as usize), appealed_outcome: OutcomeReport::Scalar(0u128), }; court.appeals.try_push(appeal_info).unwrap(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 1870e1185..bfe69b5f7 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -719,7 +719,7 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let appeal_number = court.appeals.len().saturating_add(1); - let bond = default_appeal_bond::(appeal_number); + let bond = get_appeal_bond::(appeal_number); ensure!(T::Currency::can_reserve(&who, bond), Error::::AppealBondExceedsBalance); ensure!( appeal_number < >::bound(), @@ -799,7 +799,7 @@ mod pallet { let appealed_outcome = Self::get_latest_resolved_outcome(&market_id, old_draws.as_slice())?; - let bond = default_appeal_bond::(appeal_number); + let bond = get_appeal_bond::(appeal_number); let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; court.appeals.try_push(appeal_info).map_err(|_| { @@ -1482,7 +1482,7 @@ mod pallet { impl CourtPalletApi for Pallet where T: Config {} // No-one can bound more than BalanceOf, therefore, this functions saturates - pub fn default_appeal_bond(n: usize) -> BalanceOf + pub fn get_appeal_bond(n: usize) -> BalanceOf where T: Config, { diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 3840760f1..30b463452 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -112,7 +112,7 @@ fn fill_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { .appeals .try_push(AppealInfo { backer: number, - bond: crate::default_appeal_bond::(court.appeals.len()), + bond: crate::get_appeal_bond::(court.appeals.len()), appealed_outcome, }) .unwrap(); @@ -1091,7 +1091,7 @@ fn appeal_updates_periods() { } #[test] -fn appeal_reserves_default_appeal_bond() { +fn appeal_reserves_get_appeal_bond() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); @@ -1102,7 +1102,7 @@ fn appeal_reserves_default_appeal_bond() { assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); let free_charlie_after = Balances::free_balance(CHARLIE); - let bond = crate::default_appeal_bond::(1usize); + let bond = crate::get_appeal_bond::(1usize); assert!(!bond.is_zero()); assert_eq!(free_charlie_after, free_charlie_before - bond); assert_eq!(Balances::reserved_balance(CHARLIE), bond); @@ -1375,7 +1375,7 @@ fn back_global_dispute_fails_if_needs_to_be_last_appeal() { } #[test] -fn back_global_dispute_reserves_default_appeal_bond() { +fn back_global_dispute_reserves_get_appeal_bond() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); @@ -1384,9 +1384,9 @@ fn back_global_dispute_reserves_default_appeal_bond() { fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); - let default_appeal_bond = crate::default_appeal_bond::(MaxAppeals::get() as usize); + let get_appeal_bond = crate::get_appeal_bond::(MaxAppeals::get() as usize); assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); - assert_eq!(Balances::reserved_balance(&CHARLIE), default_appeal_bond); + assert_eq!(Balances::reserved_balance(&CHARLIE), get_appeal_bond); }); } @@ -2072,10 +2072,10 @@ fn check_necessary_jurors_weight() { fn check_appeal_bond() { ExtBuilder::default().build().execute_with(|| { let appeal_bond = AppealBond::get(); - assert_eq!(crate::default_appeal_bond::(0usize), appeal_bond); - assert_eq!(crate::default_appeal_bond::(1usize), 2 * appeal_bond); - assert_eq!(crate::default_appeal_bond::(2usize), 4 * appeal_bond); - assert_eq!(crate::default_appeal_bond::(3usize), 8 * appeal_bond); + assert_eq!(crate::get_appeal_bond::(0usize), appeal_bond); + assert_eq!(crate::get_appeal_bond::(1usize), 2 * appeal_bond); + assert_eq!(crate::get_appeal_bond::(2usize), 4 * appeal_bond); + assert_eq!(crate::get_appeal_bond::(3usize), 8 * appeal_bond); }); } @@ -2144,7 +2144,7 @@ fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { .appeals .try_push(AppealInfo { backer: CHARLIE, - bond: crate::default_appeal_bond::(1usize), + bond: crate::get_appeal_bond::(1usize), appealed_outcome: appealed_outcome.clone(), }) .unwrap(); From 1d3d70781fffdbcaf84d4f5a8465c7c3c88b5569 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 10:47:51 +0200 Subject: [PATCH 205/368] improve extrinsic comments --- zrml/court/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bfe69b5f7..5d25f9cae 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -819,7 +819,9 @@ mod pallet { Ok(()) } - /// After the court is closed (resolution happened) the juror stakes can get reassigned. + /// The juror stakes get reassigned according to the plurality decision of the jurors. + /// The losing jurors get slashed and pay for the winning jurors. + /// The tardy jurors get slashed. /// /// # Arguments /// From 6e3a4d5586b0257992ce64d5e0ed4d8e8394089c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 11:42:25 +0200 Subject: [PATCH 206/368] simplify choose_multiple_weighted --- zrml/court/src/lib.rs | 100 ++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 29 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 5d25f9cae..5e6ef707f 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -938,11 +938,8 @@ mod pallet { Ok(random_set) } - pub(crate) fn choose_multiple_weighted( - jurors: &mut JurorPoolOf, - number: usize, - ) -> Result>, DispatchError> { - let total_weight = jurors + fn calculate_total_weight(jurors: &JurorPoolOf) -> u128 { + jurors .iter() .map(|pool_item| { pool_item @@ -950,12 +947,53 @@ mod pallet { .saturating_sub(pool_item.consumed_stake) .saturated_into::() }) - .sum::(); + .sum::() + } - let mut random_set = Self::get_n_random_numbers(number, total_weight)?; + /// Returns the added active lock amount. + fn update_active_lock( + juror: &T::AccountId, + selections: &BTreeMap)>, + ) -> BalanceOf { + if let Some((_, draw_lock)) = selections.get(juror) { + if let Some(mut juror_info) = >::get(&*juror) { + juror_info.active_lock = juror_info.active_lock.saturating_add(*draw_lock); + >::insert(juror, juror_info); + } else { + debug_assert!(false, "Juror should exist in the Jurors map"); + } + return *draw_lock; + } - let mut selections = BTreeMap::)>::new(); + >::zero() + } + + fn in_range(random_number: u128, lower_bound: u128, upper_bound: u128) -> bool { + debug_assert!(lower_bound <= upper_bound); + if lower_bound <= random_number && random_number < upper_bound { + return true; + } + false + } + fn update_selections( + selections: &mut BTreeMap)>, + juror: &T::AccountId, + lock: BalanceOf, + ) { + if let Some((weight, prev_lock)) = selections.get_mut(juror) { + *weight = weight.saturating_add(1); + *prev_lock = prev_lock.saturating_add(lock); + } else { + selections.insert(juror.clone(), (1, lock)); + } + } + + fn process_juror_pool( + jurors: &mut JurorPoolOf, + random_set: &mut BTreeSet, + selections: &mut BTreeMap)>, + ) { let mut current_weight = 0u128; for JurorPoolItem { stake, juror, consumed_stake } in jurors.iter_mut() { let lower_bound = current_weight; @@ -964,31 +1002,18 @@ mod pallet { // this always gets the lowest random number first and maybe removes it for random_number in random_set.clone().iter() { - if &lower_bound <= random_number && random_number < &upper_bound { - let slashable = remainder.min(T::MinJurorStake::get()); - if let Some((weight, all_slashable)) = selections.get_mut(juror) { - *weight = weight.saturating_add(1); - *all_slashable = all_slashable.saturating_add(slashable); - } else { - selections.insert(juror.clone(), (1, slashable)); - } - remainder = remainder.saturating_sub(slashable); + if Self::in_range(*random_number, lower_bound, upper_bound) { + let lock_added = remainder.min(T::MinJurorStake::get()); + Self::update_selections(selections, juror, lock_added); + remainder = remainder.saturating_sub(lock_added); random_set.remove(random_number); } else { break; } } - if let Some((_, draw_slashable)) = selections.get_mut(juror) { - *consumed_stake = consumed_stake.saturating_add(*draw_slashable); - if let Some(mut juror_info) = >::get(&*juror) { - juror_info.active_lock = - juror_info.active_lock.saturating_add(*draw_slashable); - >::insert(juror, juror_info); - } else { - debug_assert!(false, "Juror should exist in the Jurors map"); - } - } + let total_lock_added = Self::update_active_lock(juror, selections); + *consumed_stake = consumed_stake.saturating_add(total_lock_added); if random_set.is_empty() { break; @@ -996,8 +1021,12 @@ mod pallet { current_weight = upper_bound; } + } - Ok(selections + fn convert_selections_to_draws( + selections: BTreeMap)>, + ) -> Vec> { + selections .into_iter() .map(|(juror, (weight, slashable))| Draw { juror, @@ -1005,7 +1034,20 @@ mod pallet { vote: Vote::Drawn, slashable, }) - .collect()) + .collect() + } + + pub(crate) fn choose_multiple_weighted( + jurors: &mut JurorPoolOf, + number: usize, + ) -> Result>, DispatchError> { + let total_weight = Self::calculate_total_weight(jurors); + let mut random_set = Self::get_n_random_numbers(number, total_weight)?; + let mut selections = BTreeMap::)>::new(); + + Self::process_juror_pool(jurors, &mut random_set, &mut selections); + + Ok(Self::convert_selections_to_draws(selections)) } pub(crate) fn select_jurors( From 18022978c2abdc38a4da7da4deacf88b28851f49 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 11:49:23 +0200 Subject: [PATCH 207/368] improve naming --- zrml/court/src/lib.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 5e6ef707f..d6d1a27b8 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -955,14 +955,15 @@ mod pallet { juror: &T::AccountId, selections: &BTreeMap)>, ) -> BalanceOf { - if let Some((_, draw_lock)) = selections.get(juror) { + if let Some((_, total_lock_added)) = selections.get(juror) { if let Some(mut juror_info) = >::get(&*juror) { - juror_info.active_lock = juror_info.active_lock.saturating_add(*draw_lock); + juror_info.active_lock = + juror_info.active_lock.saturating_add(*total_lock_added); >::insert(juror, juror_info); } else { debug_assert!(false, "Juror should exist in the Jurors map"); } - return *draw_lock; + return *total_lock_added; } >::zero() @@ -997,15 +998,16 @@ mod pallet { let mut current_weight = 0u128; for JurorPoolItem { stake, juror, consumed_stake } in jurors.iter_mut() { let lower_bound = current_weight; - let mut remainder = stake.saturating_sub(*consumed_stake); - let upper_bound = current_weight.saturating_add(remainder.saturated_into::()); + let mut unconsumed = stake.saturating_sub(*consumed_stake); + let upper_bound = + current_weight.saturating_add(unconsumed.saturated_into::()); // this always gets the lowest random number first and maybe removes it for random_number in random_set.clone().iter() { if Self::in_range(*random_number, lower_bound, upper_bound) { - let lock_added = remainder.min(T::MinJurorStake::get()); + let lock_added = unconsumed.min(T::MinJurorStake::get()); + unconsumed = unconsumed.saturating_sub(lock_added); Self::update_selections(selections, juror, lock_added); - remainder = remainder.saturating_sub(lock_added); random_set.remove(random_number); } else { break; From b2bca892662e82c93bf3473ac1ccccaf8386cd05 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 12:44:57 +0200 Subject: [PATCH 208/368] remove back_global_dispute --- zrml/court/src/benchmarks.rs | 57 ------------------- zrml/court/src/lib.rs | 104 +++++++++-------------------------- zrml/court/src/tests.rs | 90 +++--------------------------- zrml/court/src/weights.rs | 15 ----- 4 files changed, 32 insertions(+), 234 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 263814e48..5db94cc3e 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -394,63 +394,6 @@ benchmarks! { assert_eq!(court.periods.appeal_end, new_resolve_at); } - back_global_dispute { - let d in 1..T::MaxDraws::get(); - let r in 0..62; - - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; - - let caller: T::AccountId = whitelisted_caller(); - deposit::(&caller); - let market_id = setup_court::()?; - - let mut court = >::get(market_id).unwrap(); - let appeal_end = court.periods.appeal_end; - for i in 0..r { - let market_id_i = i.saturated_into::>(); - T::DisputeResolution::add_auto_resolve(&market_id_i, appeal_end).unwrap(); - } - - T::DisputeResolution::add_auto_resolve(&market_id, appeal_end).unwrap(); - - let aggregation_end = court.periods.aggregation_end; - for i in 0..(T::MaxAppeals::get() - 1) { - let appeal_info = AppealInfo { - backer: account("backer", i, 0), - bond: crate::get_appeal_bond::(i as usize), - appealed_outcome: OutcomeReport::Scalar(0u128), - }; - court.appeals.try_push(appeal_info).unwrap(); - } - >::insert(market_id, court); - - let salt = Default::default(); - // remove last random selections of on_dispute - >::remove(market_id); - let mut draws = >::get(market_id); - for i in 0..d { - let juror: T::AccountId = account("juror", i, 0); - >::insert(&juror, JurorInfo { - stake: T::MinJurorStake::get(), - active_lock: T::MinJurorStake::get(), - }); - let outcome = OutcomeReport::Scalar(i as u128); - let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); - let draw = - Draw { - juror, - vote: Vote::Revealed { commitment, outcome, salt }, - weight: 1u32, - slashable: T::MinJurorStake::get(), - }; - draws.try_push(draw).unwrap(); - } - >::insert(market_id, draws); - - >::set_block_number(aggregation_end + 1u64.saturated_into::()); - }: _(RawOrigin::Signed(caller), market_id) - reassign_juror_stakes { let d in 1..T::MaxDraws::get(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d6d1a27b8..d64874eca 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -260,8 +260,6 @@ mod pallet { MarketAppealed { market_id: MarketIdOf, appeal_number: u32 }, /// The juror stakes have been reassigned. JurorStakesReassigned { market_id: MarketIdOf }, - /// The global dispute backing was successful. - GlobalDisputeBacked { market_id: MarketIdOf }, } #[pallet::error] @@ -323,10 +321,6 @@ mod pallet { AmountExceedsBalance, /// After the first join of the court the amount has to be higher than the current stake. AmountBelowLastJoin, - /// The maximum number of normal appeals is reached. So only allow to back a global dispute. - OnlyGlobalDisputeAppealAllowed, - /// In order to back a global dispute, it has to be the last appeal (MaxAppeals reached). - NeedsToBeLastAppeal, /// The random number generation failed. RandNumGenFailed, /// The amount is too low to kick the lowest juror out of the stake-weighted pool. @@ -702,7 +696,8 @@ mod pallet { Ok(()) } - /// Trigger an appeal for a court. + /// Trigger an appeal for a court. The last appeal does not trigger a new court round + /// but instead it marks the court mechanism for this market as failed. /// /// # Arguments /// @@ -719,12 +714,9 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let appeal_number = court.appeals.len().saturating_add(1); + ensure!(appeal_number <= T::MaxAppeals::get() as usize, Error::::MaxAppealsReached); let bond = get_appeal_bond::(appeal_number); ensure!(T::Currency::can_reserve(&who, bond), Error::::AppealBondExceedsBalance); - ensure!( - appeal_number < >::bound(), - Error::::OnlyGlobalDisputeAppealAllowed - ); let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; @@ -738,30 +730,33 @@ mod pallet { Error::::MaxAppealsReached })?; - let new_draws = Self::select_jurors(&market_id, old_draws, appeal_number)?; - let last_resolve_at = court.periods.appeal_end; - let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); - let request_block = >::get(); - debug_assert!(request_block >= now, "Request block must be greater than now."); - let round_timing = RoundTiming { - pre_vote_end: request_block, - vote_period: T::CourtVotePeriod::get(), - aggregation_period: T::CourtAggregationPeriod::get(), - appeal_period: T::CourtAppealPeriod::get(), - }; - // sets periods one after the other from now - court.update_periods(round_timing); - let new_resolve_at = court.periods.appeal_end; - debug_assert!(new_resolve_at != last_resolve_at); - let _ids_len_1 = - T::DisputeResolution::add_auto_resolve(&market_id, court.periods.appeal_end)?; + // if appeal_number == MaxAppeals, then don't start a new appeal round + if appeal_number < T::MaxAppeals::get() as usize { + let new_draws = Self::select_jurors(&market_id, old_draws, appeal_number)?; + let request_block = >::get(); + debug_assert!(request_block >= now, "Request block must be greater than now."); + let round_timing = RoundTiming { + pre_vote_end: request_block, + vote_period: T::CourtVotePeriod::get(), + aggregation_period: T::CourtAggregationPeriod::get(), + appeal_period: T::CourtAppealPeriod::get(), + }; + // sets periods one after the other from now + court.update_periods(round_timing); + let new_resolve_at = court.periods.appeal_end; + debug_assert!(new_resolve_at != last_resolve_at); + let _ids_len_1 = + T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; + >::insert(market_id, new_draws); + } + + let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; >::insert(market_id, court); - >::insert(market_id, new_draws); let appeal_number = appeal_number as u32; Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); @@ -769,56 +764,6 @@ mod pallet { Ok(()) } - /// Back the global dispute to allow the market to be resolved - /// if the last appeal outcome is disagreed on. - /// - /// # Arguments - /// - /// - `market_id`: The identifier of the court. - /// - /// # Weight - /// - /// Complexity: `O(1)` - #[pallet::weight(1_000_000_000_000)] - #[transactional] - pub fn back_global_dispute( - origin: OriginFor, - market_id: MarketIdOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let appeal_number = court.appeals.len().saturating_add(1); - ensure!(appeal_number as u32 == T::MaxAppeals::get(), Error::::NeedsToBeLastAppeal); - - let now = >::block_number(); - Self::check_appealable_market(&market_id, &court, now)?; - - // the outcome which would be resolved on is appealed (including oracle report) - let old_draws = Draws::::get(market_id); - let appealed_outcome = - Self::get_latest_resolved_outcome(&market_id, old_draws.as_slice())?; - - let bond = get_appeal_bond::(appeal_number); - let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; - - court.appeals.try_push(appeal_info).map_err(|_| { - debug_assert!(false, "Appeal bound is checked above."); - Error::::MaxAppealsReached - })?; - - T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; - - let last_resolve_at = court.periods.appeal_end; - let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); - - >::insert(market_id, court); - - Self::deposit_event(Event::GlobalDisputeBacked { market_id }); - - Ok(()) - } - /// The juror stakes get reassigned according to the plurality decision of the jurors. /// The losing jurors get slashed and pay for the winning jurors. /// The tardy jurors get slashed. @@ -1007,6 +952,7 @@ mod pallet { if Self::in_range(*random_number, lower_bound, upper_bound) { let lock_added = unconsumed.min(T::MinJurorStake::get()); unconsumed = unconsumed.saturating_sub(lock_added); + Self::update_selections(selections, juror, lock_added); random_set.remove(random_number); } else { diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 30b463452..e936eb9ea 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1253,37 +1253,18 @@ fn appeal_fails_if_appeal_bond_exceeds_balance() { } #[test] -fn appeal_fails_if_only_global_dispute_appeal_allowed() { +fn appeal_fails_if_max_appeals_reached() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); - fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + fill_appeals(&market_id, MaxAppeals::get() as usize); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); assert_noop!( Court::appeal(Origin::signed(CHARLIE), market_id), - Error::::OnlyGlobalDisputeAppealAllowed - ); - }); -} - -#[test] -fn appeal_last_works() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - fill_appeals(&market_id, (MaxAppeals::get() - 2) as usize); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); - - assert_noop!( - Court::appeal(Origin::signed(CHARLIE), market_id), - Error::::OnlyGlobalDisputeAppealAllowed + Error::::MaxAppealsReached ); }); } @@ -1350,64 +1331,7 @@ fn check_appealable_market_fails_if_not_in_appeal_period() { } #[test] -fn back_global_dispute_fails_if_court_not_found() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Court::back_global_dispute(Origin::signed(CHARLIE), 0), - Error::::CourtNotFound - ); - }); -} - -#[test] -fn back_global_dispute_fails_if_needs_to_be_last_appeal() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - assert_noop!( - Court::back_global_dispute(Origin::signed(CHARLIE), market_id), - Error::::NeedsToBeLastAppeal - ); - }); -} - -#[test] -fn back_global_dispute_reserves_get_appeal_bond() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); - - let get_appeal_bond = crate::get_appeal_bond::(MaxAppeals::get() as usize); - assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); - assert_eq!(Balances::reserved_balance(&CHARLIE), get_appeal_bond); - }); -} - -#[test] -fn back_global_dispute_emits_event() { - ExtBuilder::default().build().execute_with(|| { - let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); - - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - - fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); - - assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); - - System::assert_last_event(Event::GlobalDisputeBacked { market_id }.into()); - }); -} - -#[test] -fn back_global_dispute_removes_auto_resolve() { +fn appeal_last_appeal_just_removes_auto_resolve() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); @@ -1420,14 +1344,14 @@ fn back_global_dispute_removes_auto_resolve() { let resolve_at = court.periods.appeal_end; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); - assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![]); }); } #[test] -fn back_global_dispute_adds_last_appeal() { +fn appeal_adds_last_appeal() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); @@ -1440,7 +1364,7 @@ fn back_global_dispute_adds_last_appeal() { let appealed_outcome = Court::get_latest_resolved_outcome(&market_id, last_draws.as_slice()).unwrap(); - assert_ok!(Court::back_global_dispute(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); let court = >::get(market_id).unwrap(); assert!(court.appeals.is_full()); diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 7948fd16f..402b27fba 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -53,7 +53,6 @@ pub trait WeightInfoZeitgeist { fn denounce_vote(d: u32) -> Weight; fn reveal_vote(d: u32) -> Weight; fn appeal(d: u32, a: u32, r: u32) -> Weight; - fn back_global_dispute(d: u32, r: u32) -> Weight; fn punish_tardy_jurors(d: u32) -> Weight; fn reassign_juror_stakes(d: u32) -> Weight; } @@ -152,20 +151,6 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().writes((28_u64).saturating_mul(a.into()))) } // Storage: Court Courts (r:1 w:1) - // Storage: MarketCommons Markets (r:1 w:0) - // Storage: Court Draws (r:1 w:0) - // Storage: Balances Reserves (r:1 w:1) - // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) - fn back_global_dispute(d: u32, r: u32) -> Weight { - Weight::from_ref_time(56_987_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(182_000).saturating_mul(d.into())) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(25_000).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) - } - // Storage: Court Courts (r:1 w:1) // Storage: Court Draws (r:1 w:0) // Storage: System Account (r:2 w:2) fn punish_tardy_jurors(d: u32) -> Weight { From 8fbba7aa9b0308eb8e22ccad2fd13f3f007df01a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 14:01:02 +0200 Subject: [PATCH 209/368] unlock jurors from last draws --- zrml/court/src/lib.rs | 58 +++++++++++++++++++++++------------------ zrml/court/src/tests.rs | 41 ++++++++++++++++------------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d64874eca..d7c75f5d4 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -331,6 +331,8 @@ mod pallet { CallerNotInDraws, /// The callers balance is lower than the appeal bond. AppealBondExceedsBalance, + /// The outcome does not match the market outcome type. + OutcomeMismatch, } #[pallet::hooks] @@ -602,6 +604,9 @@ mod pallet { ) -> DispatchResult { let denouncer = ensure_signed(origin)?; + let market = T::MarketCommons::market(&market_id)?; + ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); + let juror = T::Lookup::lookup(juror)?; ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); @@ -669,6 +674,9 @@ mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; + let market = T::MarketCommons::market(&market_id)?; + ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); + ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); @@ -734,7 +742,7 @@ mod pallet { // if appeal_number == MaxAppeals, then don't start a new appeal round if appeal_number < T::MaxAppeals::get() as usize { - let new_draws = Self::select_jurors(&market_id, old_draws, appeal_number)?; + let new_draws = Self::select_jurors(appeal_number)?; let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { @@ -752,6 +760,8 @@ mod pallet { >::insert(market_id, new_draws); } + Self::unlock_jurors_from_last_draw(&market_id, old_draws); + let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; @@ -901,7 +911,7 @@ mod pallet { selections: &BTreeMap)>, ) -> BalanceOf { if let Some((_, total_lock_added)) = selections.get(juror) { - if let Some(mut juror_info) = >::get(&*juror) { + if let Some(mut juror_info) = >::get(juror) { juror_info.active_lock = juror_info.active_lock.saturating_add(*total_lock_added); >::insert(juror, juror_info); @@ -998,38 +1008,38 @@ mod pallet { Ok(Self::convert_selections_to_draws(selections)) } - pub(crate) fn select_jurors( - market_id: &MarketIdOf, - last_draws: DrawsOf, - appeal_number: usize, - ) -> Result, DispatchError> { - let mut jurors: JurorPoolOf = JurorPool::::get(); - let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); - ensure!(jurors.len() >= necessary_jurors_weight, Error::::NotEnoughJurors); - - let random_jurors = - Self::choose_multiple_weighted(&mut jurors, necessary_jurors_weight)?; - - debug_assert!( - random_jurors.len() <= T::MaxDraws::get() as usize, - "The number of randomly selected jurors should be less than or equal to \ - `MaxDraws`." - ); + fn unlock_jurors_from_last_draw(market_id: &MarketIdOf, last_draws: DrawsOf) { // keep in mind that the old draw likely contains different jurors - for old_draw in &last_draws { + for old_draw in last_draws { if let Some(mut juror_info) = >::get(&old_draw.juror) { juror_info.active_lock = juror_info.active_lock.saturating_sub(old_draw.slashable); >::insert(&old_draw.juror, juror_info); } else { log::warn!( - "Juror {:?} not found in Jurors storage (select_jurors). Market id {:?}.", + "Juror {:?} not found in Jurors storage (unlock_jurors_from_last_draw). \ + Market id {:?}.", old_draw.juror, market_id ); debug_assert!(false); } } + } + + pub(crate) fn select_jurors(appeal_number: usize) -> Result, DispatchError> { + let mut jurors: JurorPoolOf = JurorPool::::get(); + let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); + ensure!(jurors.len() >= necessary_jurors_weight, Error::::NotEnoughJurors); + + let random_jurors = + Self::choose_multiple_weighted(&mut jurors, necessary_jurors_weight)?; + + debug_assert!( + random_jurors.len() <= T::MaxDraws::get() as usize, + "The number of randomly selected jurors should be less than or equal to \ + `MaxDraws`." + ); let new_draws = >::truncate_from(random_jurors); // new appeal round should have a fresh set of draws // modified consumed_stake for each selected juror @@ -1293,11 +1303,7 @@ mod pallet { ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); let appeal_number = 0usize; - let new_draws = Self::select_jurors( - market_id, - >::truncate_from(Vec::new()), - appeal_number, - )?; + let new_draws = Self::select_jurors(appeal_number)?; let now = >::block_number(); let request_block = >::get(); diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index e936eb9ea..4f3afe72e 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -121,12 +121,14 @@ fn fill_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { Courts::::insert(market_id, court); } -fn put_alice_in_draw(market_id: crate::MarketIdOf) { +fn put_alice_in_draw(market_id: crate::MarketIdOf, stake: crate::BalanceOf) { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(market_id); assert!(!draws.is_empty()); - draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: 42u128 }; + let slashable = MinJurorStake::get(); + draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(market_id, draws); + >::insert(ALICE, JurorInfo { stake, active_lock: slashable }); } fn set_alice_after_vote( @@ -142,7 +144,7 @@ fn set_alice_after_vote( let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id); + put_alice_in_draw(market_id, amount); run_to_block(>::get() + 1); @@ -524,7 +526,7 @@ fn vote_works() { let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id); + put_alice_in_draw(market_id, amount); let old_draws = >::get(market_id); run_to_block(>::get() + 1); @@ -554,7 +556,7 @@ fn vote_overwrite_works() { let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id); + put_alice_in_draw(market_id, amount); run_to_block(>::get() + 1); @@ -653,7 +655,7 @@ fn vote_fails_if_not_in_voting_period() { let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id); + put_alice_in_draw(market_id, amount); run_to_block(>::get() + CourtVotePeriod::get() + 1); @@ -676,7 +678,7 @@ fn reveal_vote_works() { let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id); + put_alice_in_draw(market_id, amount); run_to_block(>::get() + 1); @@ -1882,8 +1884,7 @@ fn select_jurors_updates_juror_consumed_stake() { let jurors = JurorPool::::get(); let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); - let old_draws = >::get(market_id); - let new_draws = Court::select_jurors(&market_id, old_draws, appeal_number).unwrap(); + let new_draws = Court::select_jurors(appeal_number).unwrap(); let total_draw_slashable = new_draws.iter().map(|draw| draw.slashable).sum::(); let jurors = JurorPool::::get(); @@ -1894,29 +1895,33 @@ fn select_jurors_updates_juror_consumed_stake() { } #[test] -fn select_jurors_reduces_active_lock_for_old_draw() { +fn appeal_reduces_active_lock_from_old_draws() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - fill_juror_pool(); - let appeal_number = 1usize; - fill_appeals(&market_id, appeal_number); + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, _) = set_alice_after_vote(outcome); let old_draws = >::get(market_id); assert!(!old_draws.is_empty()); old_draws.iter().for_each(|draw| { let juror = draw.juror; let juror_info = >::get(juror).unwrap(); - assert_ne!(juror_info.active_lock, 0); + assert_ne!(draw.slashable, 0); assert_eq!(juror_info.active_lock, draw.slashable); }); - Court::select_jurors(&market_id, old_draws.clone(), appeal_number).unwrap(); + run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + + assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + let new_draws = >::get(market_id); old_draws.iter().for_each(|draw| { let juror = draw.juror; let juror_info = >::get(juror).unwrap(); - assert_ne!(juror_info.active_lock, draw.slashable); - assert_eq!(juror_info.active_lock, 0); + if let Some(new_draw) = new_draws.iter().find(|new_draw| new_draw.juror == juror) { + assert_eq!(new_draw.slashable, juror_info.active_lock); + } else { + assert_eq!(juror_info.active_lock, 0); + } }); }); } From 780198f3072d093f2c6dabda22571f4ae0b43ae6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 14:15:43 +0200 Subject: [PATCH 210/368] improve error description --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d7c75f5d4..4c9db2c1c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -305,7 +305,7 @@ mod pallet { JurorNotDrawn, /// The juror was drawn but did not manage to commitmently vote within the court. JurorNotVoted, - /// The juror was already denounced. This action can only happen once. + /// The juror was already denounced. VoteAlreadyDenounced, /// A juror tried to denounce herself. SelfDenounceDisallowed, From 03531febd66d0db1d62c1a75a33ffd8d9317f486 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 14:27:56 +0200 Subject: [PATCH 211/368] shorten mock periods --- primitives/src/constants/mock.rs | 8 ++++---- zrml/court/src/tests.rs | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 3d8f61437..5e77b7b8d 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -37,10 +37,10 @@ parameter_types! { pub const AppealBond: Balance = 5 * BASE; pub const AppealBondFactor: Balance = 2 * BASE; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); - pub const RequestInterval: BlockNumber = 50; - pub const CourtVotePeriod: BlockNumber = 32; - pub const CourtAggregationPeriod: BlockNumber = 32; - pub const CourtAppealPeriod: BlockNumber = 16; + pub const RequestInterval: BlockNumber = 15; + pub const CourtVotePeriod: BlockNumber = 3; + pub const CourtAggregationPeriod: BlockNumber = 4; + pub const CourtAppealPeriod: BlockNumber = 5; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const MaxAppeals: u32 = 4; pub const MaxDraws: u32 = 47; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 4f3afe72e..4c5377b13 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1060,7 +1060,6 @@ fn appeal_updates_periods() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); - let old_request_block = >::get(); let last_court = >::get(market_id).unwrap(); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); @@ -1070,10 +1069,8 @@ fn appeal_updates_periods() { let now = >::block_number(); let court = >::get(market_id).unwrap(); - let new_request_block = >::get(); - assert_eq!(old_request_block + RequestInterval::get(), new_request_block); - - let pre_vote_end = new_request_block - now; + let request_block = >::get(); + let pre_vote_end = request_block - now; assert_eq!(court.periods.pre_vote_end, now + pre_vote_end); assert_eq!(court.periods.vote_end, court.periods.pre_vote_end + CourtVotePeriod::get()); assert_eq!( From ce9a0562afdd362021a724e86c3f3d3d30d8a6ec Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 15:25:12 +0200 Subject: [PATCH 212/368] document private functions --- zrml/court/src/lib.rs | 49 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 4c9db2c1c..aa8c1c9f1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -865,6 +865,8 @@ mod pallet { where T: Config, { + // Get `n` unique and ordered random numbers from the random number generator. + // If the generator returns three times the same number in a row, an error is returned. pub(crate) fn get_n_random_numbers( n: usize, max: u128, @@ -893,7 +895,9 @@ mod pallet { Ok(random_set) } - fn calculate_total_weight(jurors: &JurorPoolOf) -> u128 { + // Get the sum of the unconsumed stake from all jurors in the pool. + // The unconsumed stake is the stake that was not already locked in previous courts. + fn get_unconsumed_stake(jurors: &JurorPoolOf) -> u128 { jurors .iter() .map(|pool_item| { @@ -905,7 +909,8 @@ mod pallet { .sum::() } - /// Returns the added active lock amount. + // Returns the added active lock amount. + // The added active lock amount is noted in the Jurors map. fn update_active_lock( juror: &T::AccountId, selections: &BTreeMap)>, @@ -924,14 +929,21 @@ mod pallet { >::zero() } - fn in_range(random_number: u128, lower_bound: u128, upper_bound: u128) -> bool { + // Returns true, if `n` is greater or equal to `lower_bound` and less than `upper_bound`. + // Returns false, otherwise. + fn in_range(n: u128, lower_bound: u128, upper_bound: u128) -> bool { debug_assert!(lower_bound <= upper_bound); - if lower_bound <= random_number && random_number < upper_bound { + if lower_bound <= n && n < upper_bound { return true; } false } + // Updates the `selections` map for the juror and the lock amount. + // If `juror` does not already exist in `selections`, + // the vote weight is set to 1 and the lock amount is initially set. + // For each call on the same juror, the vote weight is incremented by one + // and the lock amount is added to the previous amount. fn update_selections( selections: &mut BTreeMap)>, juror: &T::AccountId, @@ -945,6 +957,9 @@ mod pallet { } } + // Match the random numbers to select some jurors from the pool. + // The active lock (and consumed stake) of the selected jurors + // is increased by the random selection weight. fn process_juror_pool( jurors: &mut JurorPoolOf, random_set: &mut BTreeSet, @@ -981,6 +996,7 @@ mod pallet { } } + // Converts the `selections` map into a vector of `Draw` structs. fn convert_selections_to_draws( selections: BTreeMap)>, ) -> Vec> { @@ -995,11 +1011,14 @@ mod pallet { .collect() } + // Choose `number` of jurors from the pool randomly + // according to the weighted stake of the jurors. + // Return the random draws. pub(crate) fn choose_multiple_weighted( jurors: &mut JurorPoolOf, number: usize, ) -> Result>, DispatchError> { - let total_weight = Self::calculate_total_weight(jurors); + let total_weight = Self::get_unconsumed_stake(jurors); let mut random_set = Self::get_n_random_numbers(number, total_weight)?; let mut selections = BTreeMap::)>::new(); @@ -1008,6 +1027,8 @@ mod pallet { Ok(Self::convert_selections_to_draws(selections)) } + // Reduce the active lock of the jurors from the last draw. + // This is useful so that the jurors can thaw their non-locked stake. fn unlock_jurors_from_last_draw(market_id: &MarketIdOf, last_draws: DrawsOf) { // keep in mind that the old draw likely contains different jurors for old_draw in last_draws { @@ -1027,6 +1048,13 @@ mod pallet { } } + // Selects the jurors for the next round. + // The `consumed_stake` in `JurorPool` and `active_lock` in `Jurors` is increased + // equally according to the weight inside the `new_draws`. + // With increasing `consumed_stake` the probability to get selected + // in further court rounds shrinks. + // + // Returns the new draws. pub(crate) fn select_jurors(appeal_number: usize) -> Result, DispatchError> { let mut jurors: JurorPoolOf = JurorPool::::get(); let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); @@ -1065,6 +1093,8 @@ mod pallet { None } + // Returns OK if the market is in a valid state to be appealed. + // Returns an error otherwise. pub(crate) fn check_appealable_market( market_id: &MarketIdOf, court: &CourtOf, @@ -1091,11 +1121,13 @@ mod pallet { T::CourtPalletId::get().0 } + /// The account ID which is used to reward the correct jurors. #[inline] pub fn reward_pot(market_id: &MarketIdOf) -> T::AccountId { T::CourtPalletId::get().into_sub_account_truncating(market_id) } + /// The account ID of the treasury. #[inline] pub(crate) fn treasury_account_id() -> T::AccountId { T::TreasuryPalletId::get().into_account_truncating() @@ -1131,6 +1163,7 @@ mod pallet { .saturating_add(APPEAL_BASIS.saturating_pow(appeals_len as u32).saturating_sub(1)) } + // Slash the losers and use the slashed amount plus the reward pot to reward the winners. fn slash_losers_to_award_winners( market_id: &MarketIdOf, valid_winners_and_losers: &[(T::AccountId, OutcomeReport, BalanceOf)], @@ -1223,6 +1256,8 @@ mod pallet { Some(best_score.0.clone()) } + // Returns the outcome, on which the market would resolve + // if the current court round is the final (not appealed) court round. pub(crate) fn get_latest_resolved_outcome( market_id: &MarketIdOf, last_draws: &[DrawOf], @@ -1241,6 +1276,7 @@ mod pallet { Ok(resolved_outcome) } + // Check if the (juror, outcome, salt) combination matches the secret hash of the vote. pub(crate) fn is_valid(commitment_matcher: CommitmentMatcherOf) -> DispatchResult { // market id and current appeal number is part of salt generation // salt should be signed by the juror (market_id ++ appeal number) @@ -1260,6 +1296,9 @@ mod pallet { Ok(()) } + // Convert the raw commitment to a hashed commitment, + // and check if it matches with the secret hash of the vote. + // Otherwise return an error. pub(crate) fn get_hashed_commitment( vote: Vote, raw_commitment: RawCommitmentOf, From 6cadc38306b7afec2a0ce871afbf5c744f3b42ed Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 15:30:00 +0200 Subject: [PATCH 213/368] add assert to check max appeals --- zrml/court/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 4c5377b13..a769fdc2d 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -104,6 +104,7 @@ fn fill_juror_pool() { } fn fill_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { + assert!(appeal_number <= MaxAppeals::get() as usize); let mut court = Courts::::get(market_id).unwrap(); let mut number = 0u128; while (number as usize) < appeal_number { From 0f5c7630a5477f69dad8fcf274992684dc7c6a41 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 15:34:09 +0200 Subject: [PATCH 214/368] add stake to juror joined event --- zrml/court/src/lib.rs | 4 ++-- zrml/court/src/tests.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index aa8c1c9f1..d6726ad59 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -234,7 +234,7 @@ mod pallet { T: Config, { /// A juror has been added to the court. - JurorJoined { juror: T::AccountId }, + JurorJoined { juror: T::AccountId, stake: BalanceOf }, /// A juror prepared to exit the court. JurorPreparedExit { juror: T::AccountId }, /// A juror has been removed from the court. @@ -436,7 +436,7 @@ mod pallet { let juror_info = JurorInfoOf:: { stake: amount, active_lock }; >::insert(&who, juror_info); - Self::deposit_event(Event::JurorJoined { juror: who }); + Self::deposit_event(Event::JurorJoined { juror: who, stake: amount }); Ok(()) } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a769fdc2d..75442e8ba 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -194,7 +194,7 @@ fn join_court_successfully_stores_required_data() { let amount = 2 * BASE; let alice_free_balance_before = Balances::free_balance(ALICE); assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - System::assert_last_event(Event::JurorJoined { juror: ALICE }.into()); + System::assert_last_event(Event::JurorJoined { juror: ALICE, stake: amount }.into()); assert_eq!( Jurors::::iter().next().unwrap(), (ALICE, JurorInfo { stake: amount, active_lock: 0u128 }) From d6abc98bc93a88c6e2f1dcc960eb6b48da46596e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 15:38:56 +0200 Subject: [PATCH 215/368] correct test --- zrml/court/src/tests.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 75442e8ba..94e7207c8 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -177,9 +177,6 @@ fn exit_court_successfully_removes_a_juror_and_frees_balances() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - assert_eq!(Jurors::::iter().count(), 1); - assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE); - assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); assert_eq!(Jurors::::iter().count(), 0); From 31ed506bb0a9e86925467cad5f538b2ce1ec912f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 15:58:38 +0200 Subject: [PATCH 216/368] improve test --- zrml/court/src/tests.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 94e7207c8..4e4911c3d 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -230,12 +230,14 @@ fn join_court_works_multiple_joins() { JurorPoolItem { stake: amount, juror: BOB, consumed_stake: 0 } ] ); + assert_eq!(Jurors::::iter().count(), 2); assert_eq!( - Jurors::::iter().collect::)>>(), - vec![ - (BOB, JurorInfo { stake: amount, active_lock: 0u128 }), - (ALICE, JurorInfo { stake: amount, active_lock: 0u128 }) - ] + Jurors::::get(ALICE).unwrap(), + JurorInfo { stake: amount, active_lock: 0u128 } + ); + assert_eq!( + Jurors::::get(BOB).unwrap(), + JurorInfo { stake: amount, active_lock: 0u128 } ); let higher_amount = amount + 1; @@ -249,12 +251,14 @@ fn join_court_works_multiple_joins() { JurorPoolItem { stake: higher_amount, juror: ALICE, consumed_stake: 0 }, ] ); + assert_eq!(Jurors::::iter().count(), 2); assert_eq!( - Jurors::::iter().collect::)>>(), - vec![ - (BOB, JurorInfo { stake: amount, active_lock: 0u128 }), - (ALICE, JurorInfo { stake: higher_amount, active_lock: 0u128 }) - ] + Jurors::::get(BOB).unwrap(), + JurorInfo { stake: amount, active_lock: 0u128 } + ); + assert_eq!( + Jurors::::get(ALICE).unwrap(), + JurorInfo { stake: higher_amount, active_lock: 0u128 } ); }); } From dd1cefc95bf96a8c111ab2e1a218aed470fd831f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 16:41:20 +0200 Subject: [PATCH 217/368] update reassigned event comment --- zrml/court/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d6726ad59..ad007169e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -258,7 +258,10 @@ mod pallet { }, /// A market has been appealed. MarketAppealed { market_id: MarketIdOf, appeal_number: u32 }, - /// The juror stakes have been reassigned. + /// The juror stakes have been reassigned. The losing jurors have been slashed. + /// The winning jurors have been rewarded by the losers. + /// The losing jurors are those, who did not vote, + /// were denounced or did not reveal their vote. JurorStakesReassigned { market_id: MarketIdOf }, } From 7ba0fcd5d9cc7e65e2c3be7032cba2eca051a630 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 3 Apr 2023 16:44:18 +0200 Subject: [PATCH 218/368] correct test --- zrml/court/src/tests.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 4e4911c3d..1186b68e5 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -572,6 +572,11 @@ fn vote_overwrite_works() { let right_outcome = OutcomeReport::Scalar(42u128); let new_commitment = BlakeTwo256::hash_of(&(ALICE, right_outcome, salt)); assert_ok!(Court::vote(Origin::signed(ALICE), market_id, new_commitment)); + assert_ne!(wrong_commitment, new_commitment); + assert_eq!( + >::get(market_id)[0].vote, + Vote::Secret { commitment: new_commitment } + ); }); } From fa8cf276c6ba9827558872dcea03662189258e86 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 08:45:51 +0200 Subject: [PATCH 219/368] add assert check --- zrml/court/src/tests.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 1186b68e5..1f9396940 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -566,6 +566,10 @@ fn vote_overwrite_works() { let salt = ::Hash::default(); let wrong_commitment = BlakeTwo256::hash_of(&(ALICE, wrong_outcome, salt)); assert_ok!(Court::vote(Origin::signed(ALICE), market_id, wrong_commitment)); + assert_eq!( + >::get(market_id)[0].vote, + Vote::Secret { commitment: wrong_commitment } + ); run_blocks(1); From 9fc3fe3cfdf8ad8ef260d9cd769009a78f884a97 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 09:18:22 +0200 Subject: [PATCH 220/368] improve tests for draws --- zrml/court/src/tests.rs | 54 ++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 1f9396940..80dbc6978 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -528,7 +528,15 @@ fn vote_works() { let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id, amount); + // trick a little bit to let alice be part of the ("random") selection + let mut draws = >::get(market_id); + assert_eq!(draws.len(), 5usize); + let slashable = MinJurorStake::get(); + let alice_index = 3usize; + draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; + >::insert(market_id, draws); + >::insert(ALICE, JurorInfo { stake: amount, active_lock: slashable }); + let old_draws = >::get(market_id); run_to_block(>::get() + 1); @@ -540,12 +548,18 @@ fn vote_works() { System::assert_last_event(Event::JurorVoted { juror: ALICE, market_id, commitment }.into()); let new_draws = >::get(market_id); - assert_eq!(old_draws[1..], new_draws[1..]); - assert_eq!(old_draws[0].juror, new_draws[0].juror); - assert_eq!(old_draws[0].weight, new_draws[0].weight); - assert_eq!(old_draws[0].slashable, new_draws[0].slashable); - assert_eq!(old_draws[0].vote, Vote::Drawn); - assert_eq!(new_draws[0].vote, Vote::Secret { commitment }); + for (i, (old_draw, new_draw)) in old_draws.iter().zip(new_draws.iter()).enumerate() { + if i == alice_index { + continue; + } else { + assert_eq!(old_draw, new_draw); + } + } + assert_eq!(old_draws[alice_index].juror, new_draws[alice_index].juror); + assert_eq!(old_draws[alice_index].weight, new_draws[alice_index].weight); + assert_eq!(old_draws[alice_index].slashable, new_draws[alice_index].slashable); + assert_eq!(old_draws[alice_index].vote, Vote::Drawn); + assert_eq!(new_draws[alice_index].vote, Vote::Secret { commitment }); }); } @@ -689,7 +703,14 @@ fn reveal_vote_works() { let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id, amount); + // trick a little bit to let alice be part of the ("random") selection + let mut draws = >::get(market_id); + assert_eq!(draws.len(), 5usize); + let slashable = MinJurorStake::get(); + let alice_index = 3usize; + draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; + >::insert(market_id, draws); + >::insert(ALICE, JurorInfo { stake: amount, active_lock: slashable }); run_to_block(>::get() + 1); @@ -709,12 +730,17 @@ fn reveal_vote_works() { ); let new_draws = >::get(market_id); - assert_eq!(old_draws[1..], new_draws[1..]); - assert_eq!(old_draws[0].juror, new_draws[0].juror); - assert_eq!(old_draws[0].weight, new_draws[0].weight); - assert_eq!(old_draws[0].slashable, new_draws[0].slashable); - assert_eq!(old_draws[0].vote, Vote::Secret { commitment }); - assert_eq!(new_draws[0].vote, Vote::Revealed { commitment, outcome, salt }); + for (i, (old_draw, new_draw)) in old_draws.iter().zip(new_draws.iter()).enumerate() { + if i == alice_index { + continue; + } + assert_eq!(old_draw, new_draw); + } + assert_eq!(old_draws[alice_index].juror, new_draws[alice_index].juror); + assert_eq!(old_draws[alice_index].weight, new_draws[alice_index].weight); + assert_eq!(old_draws[alice_index].slashable, new_draws[alice_index].slashable); + assert_eq!(old_draws[alice_index].vote, Vote::Secret { commitment }); + assert_eq!(new_draws[alice_index].vote, Vote::Revealed { commitment, outcome, salt }); }); } From aced219db47124e4f907bc51d7f201be87f3684a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 09:22:43 +0200 Subject: [PATCH 221/368] add reveal vote invalid salt fails test --- zrml/court/src/tests.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 80dbc6978..b09fada69 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -827,6 +827,23 @@ fn reveal_vote_fails_for_invalid_reveal() { }); } +#[test] +fn reveal_vote_fails_for_invalid_salt() { + ExtBuilder::default().build().execute_with(|| { + let outcome = OutcomeReport::Scalar(42u128); + let (market_id, _, correct_salt) = set_alice_after_vote(outcome.clone()); + + run_blocks(CourtVotePeriod::get() + 1); + + let incorrect_salt: ::Hash = [42; 32].into(); + assert_ne!(correct_salt, incorrect_salt); + assert_noop!( + Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, incorrect_salt), + Error::::InvalidReveal + ); + }); +} + #[test] fn reveal_vote_fails_if_juror_not_voted() { ExtBuilder::default().build().execute_with(|| { From 49f05cd66edc5f84e9812c80fd4155a644b5ce1f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 09:46:30 +0200 Subject: [PATCH 222/368] denounce slash inside reassign_juror_stakes --- zrml/court/src/lib.rs | 17 +++++------------ zrml/court/src/tests.rs | 21 +++++++++++---------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index ad007169e..580c416f3 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -635,11 +635,7 @@ mod pallet { let commitment = Self::get_hashed_commitment(draw.vote, raw_commmitment)?; - let reward_pot = Self::reward_pot(&market_id); - let (imbalance, missing) = T::Currency::slash(&juror, draw.slashable); - debug_assert!(missing.is_zero(), "Could not slash all of the amount."); - T::Currency::resolve_creating(&reward_pot, imbalance); - + // slash for the misbehaviour happens in reassign_juror_stakes let raw_vote = Vote::Denounced { commitment, outcome: outcome.clone(), salt }; draws[index] = Draw { juror: juror.clone(), vote: raw_vote, ..draw }; >::insert(market_id, draws); @@ -779,7 +775,7 @@ mod pallet { /// The juror stakes get reassigned according to the plurality decision of the jurors. /// The losing jurors get slashed and pay for the winning jurors. - /// The tardy jurors get slashed. + /// The tardy or denounced jurors get slashed. /// /// # Arguments /// @@ -833,14 +829,11 @@ mod pallet { } match draw.vote { - Vote::Drawn => { - slash_juror(&draw.juror, draw.slashable); - } - Vote::Secret { commitment: _ } => { + Vote::Drawn + | Vote::Secret { commitment: _ } + | Vote::Denounced { commitment: _, outcome: _, salt: _ } => { slash_juror(&draw.juror, draw.slashable); } - // denounce extrinsic already punished the juror - Vote::Denounced { commitment: _, outcome: _, salt: _ } => (), Vote::Revealed { commitment: _, outcome, salt: _ } => { valid_winners_and_losers.push((draw.juror, outcome, draw.slashable)); } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index b09fada69..30a9b8967 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -953,10 +953,12 @@ fn denounce_vote_works() { let free_alice_after = Balances::free_balance(ALICE); let slash = old_draws[0].slashable; assert!(!slash.is_zero()); - assert_eq!(free_alice_after, free_alice_before - slash); + // slash happens in `reassign_juror_stakes` + // see `reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners` + assert_eq!(free_alice_after, free_alice_before); let pot_balance_after = Balances::free_balance(&Court::reward_pot(&market_id)); - assert_eq!(pot_balance_after, pot_balance_before + slash); + assert_eq!(pot_balance_after, pot_balance_before); }); } @@ -1497,20 +1499,19 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { assert_eq!(free_bob_after, free_bob_before - old_draws[BOB as usize].slashable); let free_charlie_after = Balances::free_balance(&CHARLIE); - assert_eq!( - free_charlie_after, - free_charlie_before - + old_draws[ALICE as usize].slashable - + old_draws[BOB as usize].slashable - + old_draws[DAVE as usize].slashable - ); + let full_slashes = old_draws[ALICE as usize].slashable + + old_draws[BOB as usize].slashable + + old_draws[DAVE as usize].slashable + + old_draws[EVE as usize].slashable; + assert_eq!(free_charlie_after, free_charlie_before + full_slashes); let free_dave_after = Balances::free_balance(&DAVE); assert_ne!(free_dave_after, free_dave_before); assert_eq!(free_dave_after, free_dave_before - old_draws[DAVE as usize].slashable); let free_eve_after = Balances::free_balance(&EVE); - assert_eq!(free_eve_after, free_eve_before); + assert_ne!(free_eve_after, free_eve_before); + assert_eq!(free_eve_after, free_eve_before - old_draws[EVE as usize].slashable); }); } From 09213def9aa5847d7c8c6b7955a9ffb4bb4d4e8a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 10:04:13 +0200 Subject: [PATCH 223/368] improve test readibility --- zrml/court/src/tests.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 30a9b8967..8647f23c1 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1126,16 +1126,19 @@ fn appeal_updates_periods() { let court = >::get(market_id).unwrap(); let request_block = >::get(); - let pre_vote_end = request_block - now; - assert_eq!(court.periods.pre_vote_end, now + pre_vote_end); - assert_eq!(court.periods.vote_end, court.periods.pre_vote_end + CourtVotePeriod::get()); + assert!(now < request_block); + assert_eq!(court.periods.pre_vote_end, request_block); + assert_eq!(court.periods.vote_end, request_block + CourtVotePeriod::get()); assert_eq!( court.periods.aggregation_end, - court.periods.vote_end + CourtAggregationPeriod::get() + request_block + CourtVotePeriod::get() + CourtAggregationPeriod::get() ); assert_eq!( court.periods.appeal_end, - court.periods.aggregation_end + CourtAppealPeriod::get() + request_block + + CourtVotePeriod::get() + + CourtAggregationPeriod::get() + + CourtAppealPeriod::get() ); assert!(last_court.periods.pre_vote_end < court.periods.pre_vote_end); From 76e286c204135d25571d184430eb5f567dcb51f6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 10:15:47 +0200 Subject: [PATCH 224/368] rename periods to cycle_ends --- zrml/court/src/benchmarks.rs | 24 +++++++++++----------- zrml/court/src/lib.rs | 26 +++++++++++------------ zrml/court/src/tests.rs | 38 +++++++++++++++++----------------- zrml/court/src/types.rs | 40 ++++++++++++++++++------------------ 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 5db94cc3e..2b8cdeea8 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -226,7 +226,7 @@ benchmarks! { let market_id = setup_court::()?; let court = >::get(market_id).unwrap(); - let pre_vote_end = court.periods.pre_vote_end; + let pre_vote = court.periods.pre_vote; fill_draws::(market_id, d)?; @@ -240,7 +240,7 @@ benchmarks! { }; >::insert(market_id, draws); - >::set_block_number(pre_vote_end + 1u64.saturated_into::()); + >::set_block_number(pre_vote + 1u64.saturated_into::()); let commitment_vote = Default::default(); }: _(RawOrigin::Signed(caller), market_id, commitment_vote) @@ -255,7 +255,7 @@ benchmarks! { let market_id = setup_court::()?; let court = >::get(market_id).unwrap(); - let pre_vote_end = court.periods.pre_vote_end; + let pre_vote = court.periods.pre_vote; fill_draws::(market_id, d)?; @@ -280,7 +280,7 @@ benchmarks! { }; >::insert(market_id, draws); - >::set_block_number(pre_vote_end + 1u64.saturated_into::()); + >::set_block_number(pre_vote + 1u64.saturated_into::()); }: _(RawOrigin::Signed(caller), market_id, denounced_juror_unlookup, outcome, salt) reveal_vote { @@ -292,7 +292,7 @@ benchmarks! { let market_id = setup_court::()?; let court = >::get(market_id).unwrap(); - let vote_end = court.periods.vote_end; + let vote = court.periods.vote; fill_draws::(market_id, d)?; @@ -315,7 +315,7 @@ benchmarks! { }; >::insert(market_id, draws); - >::set_block_number(vote_end + 1u64.saturated_into::()); + >::set_block_number(vote + 1u64.saturated_into::()); }: _(RawOrigin::Signed(caller), market_id, outcome, salt) appeal { @@ -334,14 +334,14 @@ benchmarks! { let market_id = setup_court::()?; let mut court = >::get(market_id).unwrap(); - let appeal_end = court.periods.appeal_end; + let appeal = court.periods.appeal; for i in 0..r { let market_id_i = (i + 100).saturated_into::>(); - T::DisputeResolution::add_auto_resolve(&market_id_i, appeal_end).unwrap(); + T::DisputeResolution::add_auto_resolve(&market_id_i, appeal).unwrap(); } - T::DisputeResolution::add_auto_resolve(&market_id, appeal_end).unwrap(); + T::DisputeResolution::add_auto_resolve(&market_id, appeal).unwrap(); - let aggregation_end = court.periods.aggregation_end; + let aggregation = court.periods.aggregation; for i in 0..a { let appeal_info = AppealInfo { backer: account("backer", i, 0), @@ -376,7 +376,7 @@ benchmarks! { } >::insert(market_id, draws); - >::set_block_number(aggregation_end + 1u64.saturated_into::()); + >::set_block_number(aggregation + 1u64.saturated_into::()); let now = >::block_number(); >::put(now + 1u64.saturated_into::()); @@ -391,7 +391,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller), market_id) verify { let court = >::get(market_id).unwrap(); - assert_eq!(court.periods.appeal_end, new_resolve_at); + assert_eq!(court.periods.appeal, new_resolve_at); } reassign_juror_stakes { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 580c416f3..5edd86535 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -549,7 +549,7 @@ mod pallet { let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( - court.periods.pre_vote_end < now && now <= court.periods.vote_end, + court.cycle_ends.pre_vote < now && now <= court.cycle_ends.vote, Error::::NotInVotingPeriod ); @@ -620,7 +620,7 @@ mod pallet { let now = >::block_number(); // ensure in vote period ensure!( - court.periods.pre_vote_end < now && now <= court.periods.vote_end, + court.cycle_ends.pre_vote < now && now <= court.cycle_ends.vote, Error::::NotInVotingPeriod ); @@ -680,7 +680,7 @@ mod pallet { let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( - court.periods.vote_end < now && now <= court.periods.aggregation_end, + court.cycle_ends.vote < now && now <= court.cycle_ends.aggregation, Error::::NotInAggregationPeriod ); @@ -737,7 +737,7 @@ mod pallet { Error::::MaxAppealsReached })?; - let last_resolve_at = court.periods.appeal_end; + let last_resolve_at = court.cycle_ends.appeal; // if appeal_number == MaxAppeals, then don't start a new appeal round if appeal_number < T::MaxAppeals::get() as usize { @@ -745,14 +745,14 @@ mod pallet { let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { - pre_vote_end: request_block, + pre_vote: request_block, vote_period: T::CourtVotePeriod::get(), aggregation_period: T::CourtAggregationPeriod::get(), appeal_period: T::CourtAppealPeriod::get(), }; - // sets periods one after the other from now - court.update_periods(round_timing); - let new_resolve_at = court.periods.appeal_end; + // sets cycle_ends one after the other from now + court.update_lifecycle(round_timing); + let new_resolve_at = court.cycle_ends.appeal; debug_assert!(new_resolve_at != last_resolve_at); let _ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; @@ -1104,7 +1104,7 @@ mod pallet { ); ensure!( - court.periods.aggregation_end < now && now <= court.periods.appeal_end, + court.cycle_ends.aggregation < now && now <= court.cycle_ends.appeal, Error::::NotInAppealPeriod ); @@ -1344,17 +1344,17 @@ mod pallet { let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { - pre_vote_end: request_block, + pre_vote: request_block, vote_period: T::CourtVotePeriod::get(), aggregation_period: T::CourtAggregationPeriod::get(), appeal_period: T::CourtAppealPeriod::get(), }; - // sets periods one after the other from now + // sets cycle_ends one after the other from now let court = CourtInfo::new(round_timing); let _ids_len = - T::DisputeResolution::add_auto_resolve(market_id, court.periods.appeal_end)?; + T::DisputeResolution::add_auto_resolve(market_id, court.cycle_ends.appeal)?; >::insert(market_id, new_draws); >::insert(market_id, court); @@ -1416,7 +1416,7 @@ mod pallet { ); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - Ok(Some(court.periods.appeal_end)) + Ok(Some(court.cycle_ends.appeal)) } fn has_failed( diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 8647f23c1..601287e21 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1111,7 +1111,7 @@ fn denounce_vote_fails_if_vote_already_denounced() { } #[test] -fn appeal_updates_periods() { +fn appeal_updates_cycle_ends() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); @@ -1127,24 +1127,24 @@ fn appeal_updates_periods() { let request_block = >::get(); assert!(now < request_block); - assert_eq!(court.periods.pre_vote_end, request_block); - assert_eq!(court.periods.vote_end, request_block + CourtVotePeriod::get()); + assert_eq!(court.cycle_ends.pre_vote, request_block); + assert_eq!(court.cycle_ends.vote, request_block + CourtVotePeriod::get()); assert_eq!( - court.periods.aggregation_end, + court.cycle_ends.aggregation, request_block + CourtVotePeriod::get() + CourtAggregationPeriod::get() ); assert_eq!( - court.periods.appeal_end, + court.cycle_ends.appeal, request_block + CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() ); - assert!(last_court.periods.pre_vote_end < court.periods.pre_vote_end); - assert!(last_court.periods.vote_end < court.periods.vote_end); - assert!(last_court.periods.aggregation_end < court.periods.aggregation_end); - assert!(last_court.periods.appeal_end < court.periods.appeal_end); + assert!(last_court.cycle_ends.pre_vote < court.cycle_ends.pre_vote); + assert!(last_court.cycle_ends.vote < court.cycle_ends.vote); + assert!(last_court.cycle_ends.aggregation < court.cycle_ends.aggregation); + assert!(last_court.cycle_ends.appeal < court.cycle_ends.appeal); }); } @@ -1187,14 +1187,14 @@ fn appeal_shifts_auto_resolve() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); - let resolve_at_0 = >::get(market_id).unwrap().periods.appeal_end; + let resolve_at_0 = >::get(market_id).unwrap().cycle_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![0]); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); - let resolve_at_1 = >::get(market_id).unwrap().periods.appeal_end; + let resolve_at_1 = >::get(market_id).unwrap().cycle_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_1), vec![0]); assert_ne!(resolve_at_0, resolve_at_1); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![]); @@ -1399,7 +1399,7 @@ fn appeal_last_appeal_just_removes_auto_resolve() { fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); let court = >::get(market_id).unwrap(); - let resolve_at = court.periods.appeal_end; + let resolve_at = court.cycle_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); @@ -1986,12 +1986,12 @@ fn on_dispute_creates_correct_court_info() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); let court = >::get(market_id).unwrap(); - let periods = court.periods; + let cycle_ends = court.cycle_ends; let request_block = >::get(); - assert_eq!(periods.pre_vote_end, request_block); - assert_eq!(periods.vote_end, periods.pre_vote_end + CourtVotePeriod::get()); - assert_eq!(periods.aggregation_end, periods.vote_end + CourtAggregationPeriod::get()); - assert_eq!(periods.appeal_end, periods.aggregation_end + CourtAppealPeriod::get()); + assert_eq!(cycle_ends.pre_vote, request_block); + assert_eq!(cycle_ends.vote, cycle_ends.pre_vote + CourtVotePeriod::get()); + assert_eq!(cycle_ends.aggregation, cycle_ends.vote + CourtAggregationPeriod::get()); + assert_eq!(cycle_ends.appeal, cycle_ends.aggregation + CourtAppealPeriod::get()); assert_eq!(court.status, CourtStatus::Open); assert!(court.appeals.is_empty()); }); @@ -2005,8 +2005,8 @@ fn has_failed_returns_true_for_appealable_court_too_few_jurors() { >::kill(); let market = MarketCommons::market(&market_id).unwrap(); let court = >::get(market_id).unwrap(); - let aggregation_end = court.periods.aggregation_end; - run_to_block(aggregation_end + 1); + let aggregation = court.cycle_ends.aggregation; + run_to_block(aggregation + 1); assert!(Court::has_failed(&market_id, &market).unwrap()); }); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 59678e082..5db71de5c 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -80,15 +80,15 @@ pub enum Vote { PartialEq, Eq, )] -pub struct Periods { +pub struct CycleEnds { /// The end block of the pre-vote period. - pub(crate) pre_vote_end: BlockNumber, + pub(crate) pre_vote: BlockNumber, /// The end block of the vote period. - pub(crate) vote_end: BlockNumber, + pub(crate) vote: BlockNumber, /// The end block of the aggregation period. - pub(crate) aggregation_end: BlockNumber, + pub(crate) aggregation: BlockNumber, /// The end block of the appeal period. - pub(crate) appeal_end: BlockNumber, + pub(crate) appeal: BlockNumber, } /// The status of a court case. @@ -148,11 +148,11 @@ pub struct CourtInfo { /// The list of all appeals. pub(crate) appeals: Appeals, /// The information about the lifecycle of this court case. - pub(crate) periods: Periods, + pub(crate) cycle_ends: CycleEnds, } pub struct RoundTiming { - pub(crate) pre_vote_end: BlockNumber, + pub(crate) pre_vote: BlockNumber, pub(crate) vote_period: BlockNumber, pub(crate) aggregation_period: BlockNumber, pub(crate) appeal_period: BlockNumber, @@ -162,22 +162,22 @@ impl CourtInfo { pub fn new(round_timing: RoundTiming) -> Self { - let pre_vote_end = round_timing.pre_vote_end; - let vote_end = pre_vote_end.saturating_add(round_timing.vote_period); - let aggregation_end = vote_end.saturating_add(round_timing.aggregation_period); - let appeal_end = aggregation_end.saturating_add(round_timing.appeal_period); - let periods = Periods { pre_vote_end, vote_end, aggregation_end, appeal_end }; + let pre_vote = round_timing.pre_vote; + let vote = pre_vote.saturating_add(round_timing.vote_period); + let aggregation = vote.saturating_add(round_timing.aggregation_period); + let appeal = aggregation.saturating_add(round_timing.appeal_period); + let cycle_ends = CycleEnds { pre_vote, vote, aggregation, appeal }; let status = CourtStatus::Open; - Self { status, appeals: Default::default(), periods } + Self { status, appeals: Default::default(), cycle_ends } } - pub fn update_periods(&mut self, round_timing: RoundTiming) { - self.periods.pre_vote_end = round_timing.pre_vote_end; - self.periods.vote_end = self.periods.pre_vote_end.saturating_add(round_timing.vote_period); - self.periods.aggregation_end = - self.periods.vote_end.saturating_add(round_timing.aggregation_period); - self.periods.appeal_end = - self.periods.aggregation_end.saturating_add(round_timing.appeal_period); + pub fn update_lifecycle(&mut self, round_timing: RoundTiming) { + self.cycle_ends.pre_vote = round_timing.pre_vote; + self.cycle_ends.vote = self.cycle_ends.pre_vote.saturating_add(round_timing.vote_period); + self.cycle_ends.aggregation = + self.cycle_ends.vote.saturating_add(round_timing.aggregation_period); + self.cycle_ends.appeal = + self.cycle_ends.aggregation.saturating_add(round_timing.appeal_period); } } From 1266ef3f61acbefbc2a15e34e24e94ccc0fe68dc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 11:09:25 +0200 Subject: [PATCH 225/368] add select jurors test --- zrml/court/src/tests.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 601287e21..dca528e9b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1949,6 +1949,24 @@ fn select_jurors_updates_juror_consumed_stake() { }); } +#[test_case(0usize; "first")] +#[test_case(1usize; "second")] +#[test_case(2usize; "third")] +#[test_case(3usize; "fourth")] +fn select_jurors_fails_if_not_enough_jurors(appeal_number: usize) { + ExtBuilder::default().build().execute_with(|| { + let necessary_jurors_weight = Court::necessary_jurors_weight(appeal_number); + for i in 0..(necessary_jurors_weight - 1usize) { + let amount = MinJurorStake::get() + i as u128; + let juror = (i + 1000) as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } + + assert_noop!(Court::select_jurors(appeal_number), Error::::NotEnoughJurors); + }); +} + #[test] fn appeal_reduces_active_lock_from_old_draws() { ExtBuilder::default().build().execute_with(|| { @@ -2156,7 +2174,7 @@ fn get_latest_resolved_outcome_selects_oracle_report() { } #[test] -fn random_jurors_returns_an_unique_different_subset_of_jurors() { +fn random_jurors_returns_a_unique_different_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { run_to_block(123); From 68d5817e5e45bd65b3fc177cde746b572239c5a3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 12:10:35 +0200 Subject: [PATCH 226/368] change randomness source in court tests --- Cargo.lock | 1 - zrml/court/Cargo.toml | 1 - zrml/court/src/lib.rs | 24 ++++++++------ zrml/court/src/mock.rs | 7 +--- zrml/court/src/mock_storage.rs | 13 ++++++++ zrml/court/src/tests.rs | 58 ++++++++++++++++++++++------------ 6 files changed, 67 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e363a283b..415fea58c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12914,7 +12914,6 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "pallet-randomness-collective-flip", "pallet-timestamp", "pallet-treasury", "parity-scale-codec", diff --git a/zrml/court/Cargo.toml b/zrml/court/Cargo.toml index 1b22cd3ae..094556156 100644 --- a/zrml/court/Cargo.toml +++ b/zrml/court/Cargo.toml @@ -13,7 +13,6 @@ zrml-market-commons = { default-features = false, path = "../market-commons" } [dev-dependencies] pallet-balances = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } -pallet-randomness-collective-flip = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } pallet-timestamp = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } pallet-treasury = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } sp-io = { branch = "moonbeam-polkadot-v0.9.29", git = "https://github.com/zeitgeistpm/substrate" } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 5edd86535..fee118689 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1129,6 +1129,19 @@ mod pallet { T::TreasuryPalletId::get().into_account_truncating() } + // Get a random seed based on a nonce. + pub(crate) fn get_random_seed(nonce: u64) -> [u8; 32] { + debug_assert!( + !>::block_number().is_zero(), + "When testing with the randomness of the collective flip pallet it produces a \ + underflow (block number substraction by one) panic if the block number is zero." + ); + let mut seed = [0; 32]; + let (random_hash, _) = T::Random::random(&nonce.to_le_bytes()); + seed.copy_from_slice(&random_hash.as_ref()[..32]); + seed + } + // Returns a cryptographically secure random number generator // implementation based on the seed provided by the `Config::Random` type // and the `JurorsSelectionNonce` storage. @@ -1138,15 +1151,8 @@ mod pallet { *n = n.wrapping_add(1); rslt }); - let mut seed = [0; 32]; - debug_assert!( - !>::block_number().is_zero(), - "When testing with the randomness of the collective flip pallet it produces a \ - underflow (block number substraction by one) panic if the block number is zero." - ); - let (random_hash, _) = T::Random::random(&nonce.to_le_bytes()); - seed.copy_from_slice(&random_hash.as_ref()[..32]); - ChaCha20Rng::from_seed(seed) + let random_seed = Self::get_random_seed(nonce); + ChaCha20Rng::from_seed(random_seed) } // Calculates the necessary number of draws depending on the number of market appeals. diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 224b9ccff..aa813a7c6 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -75,7 +75,6 @@ construct_runtime!( Balances: pallet_balances::{Call, Config, Event, Pallet, Storage}, Court: zrml_court::{Event, Pallet, Storage}, MarketCommons: zrml_market_commons::{Pallet, Storage}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, System: frame_system::{Call, Config, Event, Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet}, Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, @@ -152,7 +151,7 @@ impl crate::Config for Runtime { type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; type CourtPalletId = CourtPalletId; - type Random = RandomnessCollectiveFlip; + type Random = MockStorage; type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; @@ -198,8 +197,6 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); } -impl pallet_randomness_collective_flip::Config for Runtime {} - impl zrml_market_commons::Config for Runtime { type Currency = Balances; type MarketId = MarketId; @@ -270,7 +267,6 @@ impl ExtBuilder { pub fn run_to_block(n: BlockNumber) { while System::block_number() < n { Balances::on_finalize(System::block_number()); - RandomnessCollectiveFlip::on_finalize(System::block_number()); Court::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); @@ -281,7 +277,6 @@ pub fn run_to_block(n: BlockNumber) { System::initialize(&System::block_number(), &parent_block_hash, ¤t_digest); System::on_initialize(System::block_number()); Court::on_initialize(System::block_number()); - RandomnessCollectiveFlip::on_initialize(System::block_number()); Balances::on_initialize(System::block_number()); } } diff --git a/zrml/court/src/mock_storage.rs b/zrml/court/src/mock_storage.rs index 7c93b21b8..4697dc9c8 100644 --- a/zrml/court/src/mock_storage.rs +++ b/zrml/court/src/mock_storage.rs @@ -18,6 +18,10 @@ #![cfg(test)] #![allow(dead_code)] +pub use pallet::*; +use parity_scale_codec::Encode; +use sp_runtime::traits::Hash; + #[frame_support::pallet] pub(crate) mod pallet { use core::marker::PhantomData; @@ -50,3 +54,12 @@ pub(crate) mod pallet { ValueQuery, >; } + +impl frame_support::traits::Randomness for Pallet { + fn random(subject: &[u8]) -> (T::Hash, T::BlockNumber) { + let block_number = >::block_number(); + let seed = subject.using_encoded(T::Hashing::hash); + + (seed, block_number) + } +} diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index dca528e9b..d72208a2b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1263,6 +1263,9 @@ fn appeal_get_latest_resolved_outcome_changes() { let outcome = OutcomeReport::Scalar(69u128); let salt = ::Hash::default(); let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + + // cheat a little to get alice in the draw for the new appeal + put_alice_in_draw(market_id, MinJurorStake::get()); assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); run_blocks(CourtVotePeriod::get() + 1); @@ -2174,7 +2177,7 @@ fn get_latest_resolved_outcome_selects_oracle_report() { } #[test] -fn random_jurors_returns_a_unique_different_subset_of_jurors() { +fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { ExtBuilder::default().build().execute_with(|| { run_to_block(123); @@ -2187,31 +2190,46 @@ fn random_jurors_returns_a_unique_different_subset_of_jurors() { jurors.try_push(pool_item.clone()).unwrap(); } - let random_jurors = Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); - let mut at_least_one_set_is_different = false; + let nonce_0 = 42u64; + >::put(nonce_0); + // randomness is mocked and purely based on the nonce + // thus a different nonce will result in a different seed (disregarding hash collisions) + let first_random_seed = Court::get_random_seed(nonce_0); + let first_random_list = Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); - for _ in 0..100 { - run_blocks(1); + run_blocks(1); - let another_set_of_random_jurors = - Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); - let mut iter = another_set_of_random_jurors.iter(); + let nonce_1 = 69u64; + >::put(nonce_1); + let second_random_seed = Court::get_random_seed(nonce_1); - if let Some(juror) = iter.next() { - at_least_one_set_is_different = random_jurors.iter().all(|el| el != juror); - } else { - continue; - } - for juror in iter { - at_least_one_set_is_different &= random_jurors.iter().all(|el| el != juror); - } + assert_ne!(first_random_seed, second_random_seed); + let second_random_list = Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); - if at_least_one_set_is_different { - break; - } + // the two lists contain different jurors + for juror in &first_random_list { + assert!(second_random_list.iter().all(|el| el != juror)); } + }); +} + +#[test] +fn get_random_seed_returns_equal_seeds_with_equal_nonce() { + ExtBuilder::default().build().execute_with(|| { + run_to_block(123); + + // this is useful to check that the random seed only depends on the nonce + // the same nonce always results in the same seed for testing deterministic + let nonce = 42u64; + >::put(nonce); + let first_random_seed = Court::get_random_seed(nonce); + + run_blocks(1); + + >::put(nonce); + let second_random_seed = Court::get_random_seed(nonce); - assert!(at_least_one_set_is_different); + assert_eq!(first_random_seed, second_random_seed); }); } From 0a5a7d02eff6340251bdcddf52c78340ac2fcbb9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 12:30:41 +0200 Subject: [PATCH 227/368] fix clippy --- zrml/court/src/benchmarks.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 2b8cdeea8..a608fd5f9 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -226,7 +226,7 @@ benchmarks! { let market_id = setup_court::()?; let court = >::get(market_id).unwrap(); - let pre_vote = court.periods.pre_vote; + let pre_vote = court.cycle_ends.pre_vote; fill_draws::(market_id, d)?; @@ -255,7 +255,7 @@ benchmarks! { let market_id = setup_court::()?; let court = >::get(market_id).unwrap(); - let pre_vote = court.periods.pre_vote; + let pre_vote = court.cycle_ends.pre_vote; fill_draws::(market_id, d)?; @@ -292,7 +292,7 @@ benchmarks! { let market_id = setup_court::()?; let court = >::get(market_id).unwrap(); - let vote = court.periods.vote; + let vote_end = court.cycle_ends.vote; fill_draws::(market_id, d)?; @@ -315,7 +315,7 @@ benchmarks! { }; >::insert(market_id, draws); - >::set_block_number(vote + 1u64.saturated_into::()); + >::set_block_number(vote_end + 1u64.saturated_into::()); }: _(RawOrigin::Signed(caller), market_id, outcome, salt) appeal { @@ -334,14 +334,14 @@ benchmarks! { let market_id = setup_court::()?; let mut court = >::get(market_id).unwrap(); - let appeal = court.periods.appeal; + let appeal_end = court.cycle_ends.appeal; for i in 0..r { let market_id_i = (i + 100).saturated_into::>(); - T::DisputeResolution::add_auto_resolve(&market_id_i, appeal).unwrap(); + T::DisputeResolution::add_auto_resolve(&market_id_i, appeal_end).unwrap(); } - T::DisputeResolution::add_auto_resolve(&market_id, appeal).unwrap(); + T::DisputeResolution::add_auto_resolve(&market_id, appeal_end).unwrap(); - let aggregation = court.periods.aggregation; + let aggregation = court.cycle_ends.aggregation; for i in 0..a { let appeal_info = AppealInfo { backer: account("backer", i, 0), @@ -391,7 +391,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller), market_id) verify { let court = >::get(market_id).unwrap(); - assert_eq!(court.periods.appeal, new_resolve_at); + assert_eq!(court.cycle_ends.appeal, new_resolve_at); } reassign_juror_stakes { From a2a2098ab5ebb02a423b2d9f7498962162b7d4e9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 13:15:10 +0200 Subject: [PATCH 228/368] imrpove tests --- zrml/court/src/tests.rs | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index d72208a2b..189117e66 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -381,21 +381,14 @@ fn prepare_exit_court_removes_correct_jurors() { let min = MinJurorStake::get(); let min_amount = 2 * min; - let max_accounts = JurorPoolOf::::bound(); - let mut rng = rand::thread_rng(); - let mut random_numbers: Vec = (0u32..max_accounts as u32).collect(); - random_numbers.shuffle(&mut rng); - let mut random_jurors = random_numbers.clone(); - random_jurors.shuffle(&mut rng); - let max_amount = min_amount + max_accounts as u128; - for i in random_numbers { - let amount = max_amount - i as u128; - let juror = random_jurors.remove(0) as u128; + for i in 0..JurorPoolOf::::bound() { + let amount = min_amount + i as u128; + let juror = i as u128; let _ = Balances::deposit(&juror, amount).unwrap(); assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - for r in 0..max_accounts { + for r in 0..JurorPoolOf::::bound() { let len = JurorPool::::get().into_inner().len(); assert!( JurorPool::::get().into_inner().iter().any(|item| item.juror == r as u128) @@ -409,6 +402,32 @@ fn prepare_exit_court_removes_correct_jurors() { }); } +#[test] +fn join_court_binary_search_sorted_insert_works() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let min_amount = 2 * min; + + let max_accounts = JurorPoolOf::::bound(); + let mut rng = rand::thread_rng(); + let mut random_numbers: Vec = (0u32..max_accounts as u32).collect(); + random_numbers.shuffle(&mut rng); + let max_amount = min_amount + max_accounts as u128; + for i in random_numbers { + let amount = max_amount - i as u128; + let juror = i as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } + + let mut last_stake = 0; + for pool_item in JurorPool::::get().into_inner().iter() { + assert!(pool_item.stake >= last_stake); + last_stake = pool_item.stake; + } + }); +} + #[test] fn prepare_exit_court_fails_juror_already_prepared_to_exit() { ExtBuilder::default().build().execute_with(|| { From 3c48e40d6efb3a7da2c9bace21c3d034854c2707 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 13:32:57 +0200 Subject: [PATCH 229/368] remove cccount lookup in tests --- zrml/court/src/tests.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 189117e66..9309b18fe 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -26,8 +26,8 @@ use crate::{ }, mock_storage::pallet::MarketIdsPerDisputeBlock, types::{CourtStatus, Draw, Vote}, - AccountIdLookupOf, AppealInfo, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, - JurorPoolItem, JurorPoolOf, Jurors, MarketOf, RequestBlock, + AppealInfo, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, + JurorPoolOf, Jurors, MarketOf, RequestBlock, }; use frame_support::{assert_noop, assert_ok, traits::fungible::Balanced}; use pallet_balances::BalanceLock; @@ -471,8 +471,7 @@ fn exit_court_works_without_active_lock() { assert!(Jurors::::get(ALICE).is_some()); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); - let alice_lookup: AccountIdLookupOf = ALICE; - assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); + assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); System::assert_last_event( Event::JurorExited { juror: ALICE, exit_amount: amount, active_lock: 0u128 }.into(), ); @@ -499,8 +498,7 @@ fn exit_court_works_with_active_lock() { >::insert(ALICE, JurorInfo { stake: amount, active_lock }); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); - let alice_lookup: AccountIdLookupOf = ALICE; - assert_ok!(Court::exit_court(Origin::signed(ALICE), alice_lookup)); + assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); System::assert_last_event( Event::JurorExited { juror: ALICE, exit_amount: amount - active_lock, active_lock } .into(), @@ -516,9 +514,8 @@ fn exit_court_works_with_active_lock() { #[test] fn exit_court_fails_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { - let alice_lookup: AccountIdLookupOf = ALICE; assert_noop!( - Court::exit_court(Origin::signed(ALICE), alice_lookup), + Court::exit_court(Origin::signed(ALICE), ALICE), Error::::JurorDoesNotExist ); }); @@ -530,9 +527,8 @@ fn exit_court_fails_juror_not_prepared_to_exit() { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - let alice_lookup: AccountIdLookupOf = ALICE; assert_noop!( - Court::exit_court(Origin::signed(ALICE), alice_lookup), + Court::exit_court(Origin::signed(ALICE), ALICE), Error::::JurorNotPreparedToExit ); }); From 48d87f643ee0406f405a7b1202c9988b3aacfa65 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 4 Apr 2023 13:39:45 +0200 Subject: [PATCH 230/368] improve test setup --- zrml/court/src/tests.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 9309b18fe..8ac9d41df 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -487,8 +487,6 @@ fn exit_court_works_with_active_lock() { let amount = 3 * active_lock; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert!(!JurorPool::::get().into_inner().is_empty()); - assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); - assert!(JurorPool::::get().into_inner().is_empty()); assert_eq!( >::get(ALICE).unwrap(), @@ -498,6 +496,10 @@ fn exit_court_works_with_active_lock() { >::insert(ALICE, JurorInfo { stake: amount, active_lock }); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); + + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + assert!(JurorPool::::get().into_inner().is_empty()); + assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); System::assert_last_event( Event::JurorExited { juror: ALICE, exit_amount: amount - active_lock, active_lock } From c16d461f1d560b135e175732b09c05709a5cae9c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 5 Apr 2023 15:23:58 +0200 Subject: [PATCH 231/368] add API tests, add missing unlockings --- zrml/court/src/lib.rs | 8 +- zrml/court/src/tests.rs | 274 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 271 insertions(+), 11 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index fee118689..588d329c9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -757,10 +757,9 @@ mod pallet { let _ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; >::insert(market_id, new_draws); + Self::unlock_jurors_from_last_draw(&market_id, old_draws); } - Self::unlock_jurors_from_last_draw(&market_id, old_draws); - let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; @@ -1380,6 +1379,7 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let draws = Draws::::get(market_id); let resolved_outcome = Self::get_latest_resolved_outcome(market_id, draws.as_slice())?; + Self::unlock_jurors_from_last_draw(market_id, draws); court.status = CourtStatus::Closed { winner: resolved_outcome.clone() }; >::insert(market_id, court); @@ -1499,6 +1499,8 @@ mod pallet { }) .collect::>(); + let old_draws = Draws::::get(market_id); + Self::unlock_jurors_from_last_draw(market_id, old_draws); >::remove(market_id); >::remove(market_id); @@ -1511,6 +1513,8 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); + let old_draws = Draws::::get(market_id); + Self::unlock_jurors_from_last_draw(market_id, old_draws); >::remove(market_id); >::remove(market_id); diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 8ac9d41df..f67bcb04b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -18,6 +18,7 @@ #![cfg(test)] +extern crate alloc; use crate::{ mock::{ run_blocks, run_to_block, Balances, Court, ExtBuilder, MarketCommons, Origin, Runtime, @@ -26,11 +27,15 @@ use crate::{ }, mock_storage::pallet::MarketIdsPerDisputeBlock, types::{CourtStatus, Draw, Vote}, - AppealInfo, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, - JurorPoolOf, Jurors, MarketOf, RequestBlock, + AppealInfo, BalanceOf, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, + JurorPoolItem, JurorPoolOf, Jurors, MarketIdOf, MarketOf, NegativeImbalanceOf, RequestBlock, }; -use frame_support::{assert_noop, assert_ok, traits::fungible::Balanced}; -use pallet_balances::BalanceLock; +use alloc::collections::BTreeMap; +use frame_support::{ + assert_noop, assert_ok, + traits::{fungible::Balanced, tokens::imbalance::Imbalance, Currency, NamedReservableCurrency}, +}; +use pallet_balances::{BalanceLock, NegativeImbalance}; use rand::seq::SliceRandom; use sp_runtime::traits::{BlakeTwo256, Hash, Zero}; use test_case::test_case; @@ -71,7 +76,7 @@ const DEFAULT_MARKET: MarketOf = Market { bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None }, }; -fn initialize_court() -> crate::MarketIdOf { +fn initialize_court() -> MarketIdOf { let now = >::block_number(); >::put(now + RequestInterval::get()); let amount_alice = 2 * BASE; @@ -103,7 +108,7 @@ fn fill_juror_pool() { } } -fn fill_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { +fn fill_appeals(market_id: &MarketIdOf, appeal_number: usize) { assert!(appeal_number <= MaxAppeals::get() as usize); let mut court = Courts::::get(market_id).unwrap(); let mut number = 0u128; @@ -122,7 +127,7 @@ fn fill_appeals(market_id: &crate::MarketIdOf, appeal_number: usize) { Courts::::insert(market_id, court); } -fn put_alice_in_draw(market_id: crate::MarketIdOf, stake: crate::BalanceOf) { +fn put_alice_in_draw(market_id: MarketIdOf, stake: BalanceOf) { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(market_id); assert!(!draws.is_empty()); @@ -135,7 +140,7 @@ fn put_alice_in_draw(market_id: crate::MarketIdOf, stake: crate::Balanc fn set_alice_after_vote( outcome: OutcomeReport, ) -> ( - crate::MarketIdOf, + MarketIdOf, ::Hash, ::Hash, ) { @@ -1915,6 +1920,29 @@ fn on_dispute_denies_non_court_markets() { }); } +#[test] +fn on_resolution_sets_court_status() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + assert_eq!(market.report.as_ref().unwrap().outcome, ORACLE_REPORT); + + assert_eq!(Court::on_resolution(&market_id, &market), Ok(Some(ORACLE_REPORT))); + let court = >::get(market_id).unwrap(); + assert_eq!(court.status, CourtStatus::Closed { winner: ORACLE_REPORT }); + }); +} + +#[test] +fn on_resolution_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); + let market = MarketCommons::market(&market_id).unwrap(); + + assert_noop!(Court::on_resolution(&market_id, &market), Error::::CourtNotFound); + }); +} + #[test] fn on_resolution_denies_non_court_markets() { ExtBuilder::default().build().execute_with(|| { @@ -1927,6 +1955,190 @@ fn on_resolution_denies_non_court_markets() { }); } +#[test] +fn exchange_fails_if_non_court_markets() { + ExtBuilder::default().build().execute_with(|| { + let mut market = DEFAULT_MARKET; + market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + assert_noop!( + Court::exchange(&0, &market, &ORACLE_REPORT, NegativeImbalance::::zero()), + Error::::MarketDoesNotHaveCourtMechanism + ); + }); +} + +#[test] +fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + + let resolved_outcome = OutcomeReport::Scalar(1); + let other_outcome = OutcomeReport::Scalar(2); + + let mut court = >::get(market_id).unwrap(); + let mut free_balances_before = BTreeMap::new(); + let mut number = 0u128; + let mut slashed_bonds = >::zero(); + while (number as usize) < MaxAppeals::get() as usize { + let bond = crate::get_appeal_bond::(court.appeals.len()); + let appealed_outcome = if number % 2 == 0 { + // The appeals are not justified, + // because the appealed outcomes are equal to the resolved outcome. + // it is punished to appeal the right outcome + slashed_bonds += bond; + resolved_outcome.clone() + } else { + other_outcome.clone() + }; + + let backer = number; + let _ = Balances::deposit(&backer, bond).unwrap(); + assert_ok!(Balances::reserve_named(&Court::reserve_id(), &backer, bond)); + let free_balance = Balances::free_balance(&backer); + free_balances_before.insert(backer, free_balance); + court.appeals.try_push(AppealInfo { backer, bond, appealed_outcome }).unwrap(); + number += 1; + } + Courts::::insert(market_id, court); + + let imbalance: NegativeImbalanceOf = + as Currency>>::issue( + 42_000_000_000, + ); + let prev_balance = imbalance.peek(); + let imb_remainder = + Court::exchange(&market_id, &market, &resolved_outcome, imbalance).unwrap(); + assert_eq!(imb_remainder.peek(), prev_balance + slashed_bonds); + + let court = >::get(market_id).unwrap(); + let appeals = court.appeals; + for AppealInfo { backer, bond, appealed_outcome } in appeals { + assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &backer), 0); + let free_balance_after = Balances::free_balance(&backer); + let free_balance_before = free_balances_before.get(&backer).unwrap(); + + if appealed_outcome == resolved_outcome { + assert_eq!(free_balance_after, *free_balance_before); + } else { + assert_eq!(free_balance_after, *free_balance_before + bond); + } + } + }); +} + +#[test] +fn get_auto_resolve_works() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + let court = >::get(market_id).unwrap(); + let appeal_end = court.cycle_ends.appeal; + assert_eq!(Court::get_auto_resolve(&market_id, &market).unwrap(), Some(appeal_end)); + }); +} + +#[test] +fn get_auto_resolve_fails_if_wrong_dispute_mechanism() { + ExtBuilder::default().build().execute_with(|| { + let mut market = DEFAULT_MARKET; + market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + assert_noop!( + Court::get_auto_resolve(&0, &market), + Error::::MarketDoesNotHaveCourtMechanism + ); + }); +} + +#[test] +fn get_auto_resolve_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + let market = DEFAULT_MARKET; + assert_noop!(Court::get_auto_resolve(&0, &market), Error::::CourtNotFound); + }); +} + +#[test] +fn on_global_dispute_removes_court() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + assert!(>::contains_key(market_id)); + assert_ok!(Court::on_global_dispute(&market_id, &market)); + assert!(!>::contains_key(market_id)); + }); +} + +#[test] +fn on_global_dispute_removes_draws() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + assert!(>::contains_key(market_id)); + assert_ok!(Court::on_global_dispute(&market_id, &market)); + assert!(!>::contains_key(market_id)); + }); +} + +#[test] +fn on_global_dispute_fails_if_wrong_dispute_mechanism() { + ExtBuilder::default().build().execute_with(|| { + let mut market = DEFAULT_MARKET; + market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + assert_noop!( + Court::on_global_dispute(&0, &market), + Error::::MarketDoesNotHaveCourtMechanism + ); + }); +} + +#[test] +fn on_global_dispute_fails_if_court_not_found() { + ExtBuilder::default().build().execute_with(|| { + let market = DEFAULT_MARKET; + assert_noop!(Court::on_global_dispute(&0, &market), Error::::CourtNotFound); + }); +} + +#[test] +fn on_global_dispute_fails_if_market_report_not_found() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + MarketCommons::mutate_market(&market_id, |market| { + market.report = None; + Ok(()) + }) + .unwrap(); + let market = MarketCommons::market(&market_id).unwrap(); + assert_noop!( + Court::on_global_dispute(&market_id, &market), + Error::::MarketReportNotFound + ); + }); +} + +#[test] +fn on_global_dispute_returns_appealed_outcomes() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + let mut court = >::get(market_id).unwrap(); + let mut gd_outcomes = Vec::new(); + + let initial_vote_amount = >::zero(); + let treasury_account = Court::treasury_account_id(); + for number in 0..MaxAppeals::get() { + let appealed_outcome = OutcomeReport::Scalar(number as u128); + let backer = number as u128; + let bond = crate::get_appeal_bond::(court.appeals.len()); + gd_outcomes.push((appealed_outcome.clone(), treasury_account, initial_vote_amount)); + court.appeals.try_push(AppealInfo { backer, bond, appealed_outcome }).unwrap(); + } + Courts::::insert(market_id, court); + assert_eq!(Court::on_global_dispute(&market_id, &market).unwrap(), gd_outcomes); + }); +} + #[test] fn choose_multiple_weighted_works() { ExtBuilder::default().build().execute_with(|| { @@ -2035,6 +2247,50 @@ fn on_dispute_creates_correct_court_info() { }); } +#[test] +fn on_dispute_fails_if_court_already_exists() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let market = MarketCommons::market(&market_id).unwrap(); + assert_noop!(Court::on_dispute(&market_id, &market), Error::::CourtAlreadyExists); + }); +} + +#[test] +fn on_dispute_inserts_draws() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let draws = >::get(market_id); + assert_eq!( + draws[0], + Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } + ); + assert_eq!( + draws[1], + Draw { juror: BOB, weight: 2, vote: Vote::Drawn, slashable: 2 * MinJurorStake::get() } + ); + assert_eq!( + draws[2], + Draw { juror: DAVE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } + ); + assert_eq!( + draws[3], + Draw { juror: EVE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } + ); + assert_eq!(draws.len(), 4usize); + }); +} + +#[test] +fn on_dispute_adds_auto_resolve() { + ExtBuilder::default().build().execute_with(|| { + let market_id = initialize_court(); + let court = >::get(market_id).unwrap(); + let resolve_at = court.cycle_ends.appeal; + assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); + }); +} + #[test] fn has_failed_returns_true_for_appealable_court_too_few_jurors() { ExtBuilder::default().build().execute_with(|| { @@ -2101,7 +2357,7 @@ fn check_appeal_bond() { }); } -fn prepare_draws(market_id: &crate::MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { +fn prepare_draws(market_id: &MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { let mut draws: crate::DrawsOf = vec![].try_into().unwrap(); for (i, (outcome_index, weight)) in outcomes_with_weights.iter().enumerate() { // offset to not conflict with other jurors From a8452fd2c2341bb862049c5098451e769dbe3769 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 6 Apr 2023 12:44:49 +0200 Subject: [PATCH 232/368] add full cycle court tests --- primitives/Cargo.toml | 1 - runtime/battery-station/Cargo.toml | 11 +- runtime/battery-station/src/lib.rs | 7 - runtime/battery-station/src/parameters.rs | 1 - runtime/common/Cargo.toml | 1 - runtime/common/src/lib.rs | 10 +- runtime/zeitgeist/Cargo.toml | 11 +- runtime/zeitgeist/src/lib.rs | 6 - runtime/zeitgeist/src/parameters.rs | 1 - scripts/benchmarks/configuration.sh | 2 +- scripts/benchmarks/quick_check.sh | 3 +- scripts/tests/misc.sh | 3 +- zrml/court/src/lib.rs | 7 +- zrml/court/src/types.rs | 56 ++-- zrml/prediction-markets/Cargo.toml | 7 +- zrml/prediction-markets/src/benchmarks.rs | 14 +- zrml/prediction-markets/src/lib.rs | 129 ++++---- zrml/prediction-markets/src/mock.rs | 33 +- zrml/prediction-markets/src/tests.rs | 385 ++++++++++++++++++---- 19 files changed, 438 insertions(+), 250 deletions(-) diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 38ffb0fac..f2e9e0270 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -28,7 +28,6 @@ std = [ "sp-core/std", "sp-runtime/std", ] -with-global-disputes = [] [package] authors = ["Zeitgeist PM "] diff --git a/runtime/battery-station/Cargo.toml b/runtime/battery-station/Cargo.toml index 5748d0ff0..5a053e528 100644 --- a/runtime/battery-station/Cargo.toml +++ b/runtime/battery-station/Cargo.toml @@ -106,7 +106,7 @@ common-runtime = { default-features = false, path = "../common" } zeitgeist-primitives = { default-features = false, path = "../../primitives" } zrml-authorized = { default-features = false, path = "../../zrml/authorized" } zrml-court = { default-features = false, path = "../../zrml/court" } -zrml-global-disputes = { default-features = false, path = "../../zrml/global-disputes", optional = true } +zrml-global-disputes = { default-features = false, path = "../../zrml/global-disputes" } zrml-liquidity-mining = { default-features = false, path = "../../zrml/liquidity-mining" } zrml-market-commons = { default-features = false, path = "../../zrml/market-commons" } zrml-prediction-markets = { default-features = false, path = "../../zrml/prediction-markets" } @@ -317,7 +317,7 @@ std = [ "zrml-prediction-markets/std", "zrml-rikiddo/std", "zrml-simple-disputes/std", - "zrml-global-disputes?/std", + "zrml-global-disputes/std", "zrml-styx/std", "zrml-swaps-runtime-api/std", "zrml-swaps/std", @@ -368,7 +368,7 @@ try-runtime = [ "zrml-prediction-markets/try-runtime", "zrml-rikiddo/try-runtime", "zrml-simple-disputes/try-runtime", - "zrml-global-disputes?/try-runtime", + "zrml-global-disputes/try-runtime", "zrml-styx/try-runtime", "zrml-swaps/try-runtime", @@ -392,11 +392,6 @@ try-runtime = [ "cumulus-pallet-xcmp-queue?/try-runtime", "parachain-info?/try-runtime", ] -with-global-disputes = [ - "zrml-global-disputes", - "common-runtime/with-global-disputes", - "zrml-prediction-markets/with-global-disputes", -] [package] authors = ["Zeitgeist PM "] diff --git a/runtime/battery-station/src/lib.rs b/runtime/battery-station/src/lib.rs index d9c6dcca2..c9c005344 100644 --- a/runtime/battery-station/src/lib.rs +++ b/runtime/battery-station/src/lib.rs @@ -125,13 +125,6 @@ impl Contains for IsCallable { decl_common_types!(); -#[cfg(feature = "with-global-disputes")] -create_runtime_with_additional_pallets!( - GlobalDisputes: zrml_global_disputes::{Call, Event, Pallet, Storage} = 59, - Sudo: pallet_sudo::{Call, Config, Event, Pallet, Storage} = 150, -); - -#[cfg(not(feature = "with-global-disputes"))] create_runtime_with_additional_pallets!( Sudo: pallet_sudo::{Call, Config, Event, Pallet, Storage} = 150, ); diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 652131e5a..6d461069a 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -374,7 +374,6 @@ parameter_types! { pub const MinVestedTransfer: Balance = ExistentialDeposit::get(); } -#[cfg(feature = "with-global-disputes")] parameter_types! { // Global Disputes /// Vote lock identifier, mainly used for the LockableCurrency on the native token. diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index fae491e83..c68ba712d 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -67,7 +67,6 @@ std = [ "pallet-vesting/std", "pallet-parachain-staking?/std", ] -with-global-disputes = [] [package] authors = ["Zeitgeist PM "] diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 9702d36bf..846d03284 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -186,6 +186,7 @@ macro_rules! decl_common_types { let mut pallets = vec![ AuthorizedPalletId::get(), CourtPalletId::get(), + GlobalDisputesPalletId::get(), LiquidityMiningPalletId::get(), PmPalletId::get(), SimpleDisputesPalletId::get(), @@ -193,9 +194,6 @@ macro_rules! decl_common_types { TreasuryPalletId::get(), ]; - #[cfg(feature = "with-global-disputes")] - pallets.push(GlobalDisputesPalletId::get()); - if let Some(pallet_id) = frame_support::PalletId::try_from_sub_account::(ai) { return pallets.contains(&pallet_id.0); } @@ -321,6 +319,7 @@ macro_rules! create_runtime { Swaps: zrml_swaps::{Call, Event, Pallet, Storage} = 56, PredictionMarkets: zrml_prediction_markets::{Call, Event, Pallet, Storage} = 57, Styx: zrml_styx::{Call, Event, Pallet, Storage} = 58, + GlobalDisputes: zrml_global_disputes::{Call, Event, Pallet, Storage} = 59, $($additional_pallets)* } @@ -1014,9 +1013,7 @@ macro_rules! impl_config_traits { type DestroyOrigin = EnsureRootOrAllAdvisoryCommittee; type DisputeBond = DisputeBond; type Event = Event; - #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; - #[cfg(feature = "with-global-disputes")] type GlobalDisputePeriod = GlobalDisputePeriod; // LiquidityMining is currently unstable. // NoopLiquidityMining will be applied only to mainnet once runtimes are separated. @@ -1078,7 +1075,6 @@ macro_rules! impl_config_traits { type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } - #[cfg(feature = "with-global-disputes")] impl zrml_global_disputes::Config for Runtime { type Currency = Balances; type Event = Event; @@ -1228,7 +1224,6 @@ macro_rules! create_runtime_api { list_benchmark!(list, extra, zrml_authorized, Authorized); list_benchmark!(list, extra, zrml_court, Court); list_benchmark!(list, extra, zrml_simple_disputes, SimpleDisputes); - #[cfg(feature = "with-global-disputes")] list_benchmark!(list, extra, zrml_global_disputes, GlobalDisputes); #[cfg(not(feature = "parachain"))] list_benchmark!(list, extra, zrml_prediction_markets, PredictionMarkets); @@ -1307,7 +1302,6 @@ macro_rules! create_runtime_api { add_benchmark!(params, batches, zrml_authorized, Authorized); add_benchmark!(params, batches, zrml_court, Court); add_benchmark!(params, batches, zrml_simple_disputes, SimpleDisputes); - #[cfg(feature = "with-global-disputes")] add_benchmark!(params, batches, zrml_global_disputes, GlobalDisputes); #[cfg(not(feature = "parachain"))] add_benchmark!(params, batches, zrml_prediction_markets, PredictionMarkets); diff --git a/runtime/zeitgeist/Cargo.toml b/runtime/zeitgeist/Cargo.toml index fd63175bf..0131c71aa 100644 --- a/runtime/zeitgeist/Cargo.toml +++ b/runtime/zeitgeist/Cargo.toml @@ -104,7 +104,7 @@ common-runtime = { default-features = false, path = "../common" } zeitgeist-primitives = { default-features = false, path = "../../primitives" } zrml-authorized = { default-features = false, path = "../../zrml/authorized" } zrml-court = { default-features = false, path = "../../zrml/court" } -zrml-global-disputes = { default-features = false, path = "../../zrml/global-disputes", optional = true } +zrml-global-disputes = { default-features = false, path = "../../zrml/global-disputes" } zrml-liquidity-mining = { default-features = false, path = "../../zrml/liquidity-mining" } zrml-market-commons = { default-features = false, path = "../../zrml/market-commons" } zrml-prediction-markets = { default-features = false, path = "../../zrml/prediction-markets" } @@ -308,7 +308,7 @@ std = [ "zrml-prediction-markets/std", "zrml-rikiddo/std", "zrml-simple-disputes/std", - "zrml-global-disputes?/std", + "zrml-global-disputes/std", "zrml-swaps-runtime-api/std", "zrml-styx/std", "zrml-swaps/std", @@ -359,7 +359,7 @@ try-runtime = [ "zrml-prediction-markets/try-runtime", "zrml-rikiddo/try-runtime", "zrml-simple-disputes/try-runtime", - "zrml-global-disputes?/try-runtime", + "zrml-global-disputes/try-runtime", "zrml-styx/try-runtime", "zrml-swaps/try-runtime", @@ -382,11 +382,6 @@ try-runtime = [ "cumulus-pallet-xcmp-queue?/try-runtime", "parachain-info?/try-runtime", ] -with-global-disputes = [ - "zrml-global-disputes", - "common-runtime/with-global-disputes", - "zrml-prediction-markets/with-global-disputes", -] [package] authors = ["Zeitgeist PM "] diff --git a/runtime/zeitgeist/src/lib.rs b/runtime/zeitgeist/src/lib.rs index e143564cb..b89e466b3 100644 --- a/runtime/zeitgeist/src/lib.rs +++ b/runtime/zeitgeist/src/lib.rs @@ -193,12 +193,6 @@ impl Contains for IsCallable { decl_common_types!(); -#[cfg(feature = "with-global-disputes")] -create_runtime_with_additional_pallets!( - GlobalDisputes: zrml_global_disputes::{Call, Event, Pallet, Storage} = 59, -); - -#[cfg(not(feature = "with-global-disputes"))] create_runtime_with_additional_pallets!(); impl_config_traits!(); diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 43463e7b3..317caa09a 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -374,7 +374,6 @@ parameter_types! { pub const MinVestedTransfer: Balance = ExistentialDeposit::get(); } -#[cfg(feature = "with-global-disputes")] parameter_types! { // Global Disputes /// Vote lock identifier, mainly used for the LockableCurrency on the native token. diff --git a/scripts/benchmarks/configuration.sh b/scripts/benchmarks/configuration.sh index e0e49becb..1742d8667 100644 --- a/scripts/benchmarks/configuration.sh +++ b/scripts/benchmarks/configuration.sh @@ -41,4 +41,4 @@ else fi export EXECUTION="${EXECUTION:-wasm}" export ADDITIONAL="${ADDITIONAL:-}" -export ADDITIONAL_FEATURES="${ADDITIONAL_FEATURES:-with-global-disputes}" +export ADDITIONAL_FEATURES="${ADDITIONAL_FEATURES:-}" diff --git a/scripts/benchmarks/quick_check.sh b/scripts/benchmarks/quick_check.sh index a5487dcc0..e135d625f 100755 --- a/scripts/benchmarks/quick_check.sh +++ b/scripts/benchmarks/quick_check.sh @@ -25,7 +25,6 @@ export PROFILE=release export PROFILE_DIR=release export ADDITIONAL=--detailed-log-output export EXECUTION=native -# TODO(#848) Delete this, when global disputes is on main-net -export ADDITIONAL_FEATURES=with-global-disputes +export ADDITIONAL_FEATURES="" source ./scripts/benchmarks/run_benchmarks.sh diff --git a/scripts/tests/misc.sh b/scripts/tests/misc.sh index 9431faf60..da933fe61 100755 --- a/scripts/tests/misc.sh +++ b/scripts/tests/misc.sh @@ -20,8 +20,7 @@ no_runtime_benchmarks=('court' 'market-commons' 'rikiddo') cargo test --package zeitgeist-runtime --lib -- --nocapture -# TODO(#848): Delete when feature "with-global-dispute" is removed -cargo test -p zrml-prediction-markets --features with-global-disputes,parachain +cargo test -p zrml-prediction-markets --features parachain for package in zrml/* diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 588d329c9..acd6ada3f 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -28,7 +28,7 @@ pub mod migrations; mod mock; mod mock_storage; mod tests; -mod types; +pub mod types; pub mod weights; pub use court_pallet_api::CourtPalletApi; @@ -1103,7 +1103,7 @@ mod pallet { ); ensure!( - court.cycle_ends.aggregation < now && now <= court.cycle_ends.appeal, + court.cycle_ends.aggregation < now && now < court.cycle_ends.appeal, Error::::NotInAppealPeriod ); @@ -1421,8 +1421,7 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - Ok(Some(court.cycle_ends.appeal)) + Ok(>::get(market_id).map(|court| court.cycle_ends.appeal)) } fn has_failed( diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 5db71de5c..978ebe53c 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -31,20 +31,20 @@ use zeitgeist_primitives::types::OutcomeReport; pub struct JurorInfo { /// The juror's amount in the stake weighted pool. /// This amount is used to find a juror with a binary search on the pool. - pub(crate) stake: Balance, + pub stake: Balance, /// The current amount of funds which are locked in courts. - pub(crate) active_lock: Balance, + pub active_lock: Balance, } pub struct RawCommitment { - pub(crate) juror: AccountId, - pub(crate) outcome: OutcomeReport, - pub(crate) salt: Hash, + pub juror: AccountId, + pub outcome: OutcomeReport, + pub salt: Hash, } pub struct CommitmentMatcher { - pub(crate) hashed: Hash, - pub(crate) raw: RawCommitment, + pub hashed: Hash, + pub raw: RawCommitment, } /// All possible states of a vote. @@ -82,13 +82,13 @@ pub enum Vote { )] pub struct CycleEnds { /// The end block of the pre-vote period. - pub(crate) pre_vote: BlockNumber, + pub pre_vote: BlockNumber, /// The end block of the vote period. - pub(crate) vote: BlockNumber, + pub vote: BlockNumber, /// The end block of the aggregation period. - pub(crate) aggregation: BlockNumber, + pub aggregation: BlockNumber, /// The end block of the appeal period. - pub(crate) appeal: BlockNumber, + pub appeal: BlockNumber, } /// The status of a court case. @@ -124,11 +124,11 @@ pub enum CourtStatus { )] pub struct AppealInfo { /// The account which made the appeal. - pub(crate) backer: AccountId, + pub backer: AccountId, /// The amount of funds which were locked for the appeal. - pub(crate) bond: Balance, + pub bond: Balance, /// The outcome which was appealed. - pub(crate) appealed_outcome: OutcomeReport, + pub appealed_outcome: OutcomeReport, } /// The information about a court case. @@ -144,18 +144,18 @@ pub struct AppealInfo { )] pub struct CourtInfo { /// The status of the court case. - pub(crate) status: CourtStatus, + pub status: CourtStatus, /// The list of all appeals. - pub(crate) appeals: Appeals, + pub appeals: Appeals, /// The information about the lifecycle of this court case. - pub(crate) cycle_ends: CycleEnds, + pub cycle_ends: CycleEnds, } pub struct RoundTiming { - pub(crate) pre_vote: BlockNumber, - pub(crate) vote_period: BlockNumber, - pub(crate) aggregation_period: BlockNumber, - pub(crate) appeal_period: BlockNumber, + pub pre_vote: BlockNumber, + pub vote_period: BlockNumber, + pub aggregation_period: BlockNumber, + pub appeal_period: BlockNumber, } impl @@ -195,15 +195,15 @@ impl )] pub struct Draw { /// The juror who was randomly selected. - pub(crate) juror: AccountId, + pub juror: AccountId, /// The weight of the juror in this court case. /// The higher the weight the more voice the juror has in the final winner decision. - pub(crate) weight: u32, + pub weight: u32, /// The information about the vote state. - pub(crate) vote: Vote, + pub vote: Vote, /// The amount of funds which can be slashed for this court case. /// This is related to a multiple of `MinStake` to mitigate Sybil attacks. - pub(crate) slashable: Balance, + pub slashable: Balance, } /// All information related to one item in the stake weighted juror pool. @@ -219,10 +219,10 @@ pub struct Draw { )] pub struct JurorPoolItem { /// The amount of funds associated to a juror in order to get selected for a court case. - pub(crate) stake: Balance, + pub stake: Balance, /// The account which is the juror that might be selected in court cases. - pub(crate) juror: AccountId, + pub juror: AccountId, /// The consumed amount of the stake for all draws. This is useful to reduce the probability /// of a juror to be selected again. - pub(crate) consumed_stake: Balance, + pub consumed_stake: Balance, } diff --git a/zrml/prediction-markets/Cargo.toml b/zrml/prediction-markets/Cargo.toml index f12cf796f..9f0726b99 100644 --- a/zrml/prediction-markets/Cargo.toml +++ b/zrml/prediction-markets/Cargo.toml @@ -11,7 +11,7 @@ sp-runtime = { branch = "moonbeam-polkadot-v0.9.29", default-features = false, g zeitgeist-primitives = { default-features = false, path = "../../primitives" } zrml-authorized = { default-features = false, path = "../authorized" } zrml-court = { default-features = false, path = "../court" } -zrml-global-disputes = { default-features = false, path = "../global-disputes", optional = true } +zrml-global-disputes = { default-features = false, path = "../global-disputes" } zrml-liquidity-mining = { default-features = false, path = "../liquidity-mining" } zrml-market-commons = { default-features = false, path = "../market-commons" } zrml-simple-disputes = { default-features = false, path = "../simple-disputes" } @@ -84,15 +84,12 @@ std = [ "zrml-liquidity-mining/std", "zrml-market-commons/std", "zrml-simple-disputes/std", - "zrml-global-disputes?/std", + "zrml-global-disputes/std", "xcm?/std", ] try-runtime = [ "frame-support/try-runtime", ] -with-global-disputes = [ - "zrml-global-disputes", -] [package] authors = ["Zeitgeist PM "] diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 0b149e00b..47134cef8 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -853,20 +853,14 @@ benchmarks! { let current_block: T::BlockNumber = (max_dispute_len + 1).saturated_into(); >::set_block_number(current_block); - #[cfg(feature = "with-global-disputes")] - { - let global_dispute_end = current_block + T::GlobalDisputePeriod::get(); - // the complexity depends on MarketIdsPerDisputeBlock at the current block - // this is because a variable number of market ids need to be decoded from the storage - MarketIdsPerDisputeBlock::::insert(global_dispute_end, market_ids_1); - } + let global_dispute_end = current_block + T::GlobalDisputePeriod::get(); + // the complexity depends on MarketIdsPerDisputeBlock at the current block + // this is because a variable number of market ids need to be decoded from the storage + MarketIdsPerDisputeBlock::::insert(global_dispute_end, market_ids_1); let call = Call::::start_global_dispute { market_id }; }: { - #[cfg(feature = "with-global-disputes")] call.dispatch_bypass_filter(RawOrigin::Signed(caller).into())?; - #[cfg(not(feature = "with-global-disputes"))] - let _ = call.dispatch_bypass_filter(RawOrigin::Signed(caller).into()); } dispute_authorized { diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 940565e93..ef7d6f665 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -69,7 +69,6 @@ mod pallet { Report, ScalarPosition, ScoringRule, SubsidyUntil, }, }; - #[cfg(feature = "with-global-disputes")] use zrml_global_disputes::GlobalDisputesPalletApi; use zrml_liquidity_mining::LiquidityMiningPalletApi; @@ -1420,82 +1419,77 @@ mod pallet { ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - #[cfg(feature = "with-global-disputes")] - { - let market = >::market(&market_id)?; - ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); - - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, - Error::::InvalidDisputeMechanism - ); - - ensure!( - T::GlobalDisputes::is_not_started(&market_id), - Error::::GlobalDisputeAlreadyStarted - ); + let market = >::market(&market_id)?; + ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); - let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; + ensure!( + matches!( + market.dispute_mechanism, + MarketDisputeMechanism::SimpleDisputes | MarketDisputeMechanism::Court + ), + Error::::InvalidDisputeMechanism + ); - let has_failed = match market.dispute_mechanism { - MarketDisputeMechanism::Authorized => { - T::Authorized::has_failed(&market_id, &market)? - } - MarketDisputeMechanism::Court => T::Court::has_failed(&market_id, &market)?, - MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::has_failed(&market_id, &market)? - } - }; - ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); + ensure!( + T::GlobalDisputes::is_not_started(&market_id), + Error::::GlobalDisputeAlreadyStarted + ); - let initial_vote_outcomes = match market.dispute_mechanism { - MarketDisputeMechanism::Authorized => { - T::Authorized::on_global_dispute(&market_id, &market)? - } - MarketDisputeMechanism::Court => { - T::Court::on_global_dispute(&market_id, &market)? - } - MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_global_dispute(&market_id, &market)? - } - }; + let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; - T::GlobalDisputes::push_voting_outcome( - &market_id, - report.outcome.clone(), - &report.by, - >::zero(), - )?; + let has_failed = match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + T::Authorized::has_failed(&market_id, &market)? + } + MarketDisputeMechanism::Court => T::Court::has_failed(&market_id, &market)?, + MarketDisputeMechanism::SimpleDisputes => { + T::SimpleDisputes::has_failed(&market_id, &market)? + } + }; + ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); - // push vote outcomes other than the report outcome - for (outcome, owner, bond) in initial_vote_outcomes { - T::GlobalDisputes::push_voting_outcome(&market_id, outcome, &owner, bond)?; + let initial_vote_outcomes = match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + T::Authorized::on_global_dispute(&market_id, &market)? + } + MarketDisputeMechanism::Court => T::Court::on_global_dispute(&market_id, &market)?, + MarketDisputeMechanism::SimpleDisputes => { + T::SimpleDisputes::on_global_dispute(&market_id, &market)? } + }; - // TODO(#372): Allow court with global disputes. - // ensure, that global disputes controls the resolution now - // it does not end after the dispute period now, but after the global dispute end + T::GlobalDisputes::push_voting_outcome( + &market_id, + report.outcome.clone(), + &report.by, + >::zero(), + )?; - // ignore first of tuple because we always have max disputes - let (_, ids_len_2) = Self::clear_auto_resolve(&market_id)?; + // push vote outcomes other than the report outcome + for (outcome, owner, bond) in initial_vote_outcomes { + T::GlobalDisputes::push_voting_outcome(&market_id, outcome, &owner, bond)?; + } - let now = >::block_number(); - let global_dispute_end = now.saturating_add(T::GlobalDisputePeriod::get()); - let market_ids_len = >::try_mutate( - global_dispute_end, - |ids| -> Result { - ids.try_push(market_id).map_err(|_| >::StorageOverflow)?; - Ok(ids.len() as u32) - }, - )?; + // TODO(#372): Allow court with global disputes. + // ensure, that global disputes controls the resolution now + // it does not end after the dispute period now, but after the global dispute end - Self::deposit_event(Event::GlobalDisputeStarted(market_id)); + // ignore first of tuple because we always have max disputes + let (_, ids_len_2) = Self::clear_auto_resolve(&market_id)?; - Ok(Some(T::WeightInfo::start_global_dispute(market_ids_len, ids_len_2)).into()) - } + let now = >::block_number(); + let global_dispute_end = now.saturating_add(T::GlobalDisputePeriod::get()); + let market_ids_len = >::try_mutate( + global_dispute_end, + |ids| -> Result { + ids.try_push(market_id).map_err(|_| >::StorageOverflow)?; + Ok(ids.len() as u32) + }, + )?; + + Self::deposit_event(Event::GlobalDisputeStarted(market_id)); - #[cfg(not(feature = "with-global-disputes"))] - Err(Error::::GlobalDisputesDisabled.into()) + Ok(Some(T::WeightInfo::start_global_dispute(market_ids_len, ids_len_2)).into()) } } @@ -1564,7 +1558,6 @@ mod pallet { type Event: From> + IsType<::Event>; /// See [`GlobalDisputesPalletApi`]. - #[cfg(feature = "with-global-disputes")] type GlobalDisputes: GlobalDisputesPalletApi< MarketIdOf, Self::AccountId, @@ -1572,7 +1565,6 @@ mod pallet { >; /// The number of blocks the global dispute period remains open. - #[cfg(feature = "with-global-disputes")] type GlobalDisputePeriod: Get; type LiquidityMining: LiquidityMiningPalletApi< @@ -1694,8 +1686,6 @@ mod pallet { EditorNotCreator, /// EditReason's length greater than MaxEditReasonLen. EditReasonLengthExceedsMaxEditReasonLen, - /// The global dispute resolution system is disabled. - GlobalDisputesDisabled, /// Market account does not have enough funds to pay out. InsufficientFundsInMarketAccount, /// Sender does not have enough share balance. @@ -2543,7 +2533,6 @@ mod pallet { ) -> Result { let mut resolved_outcome_option = None; - #[cfg(feature = "with-global-disputes")] if let Some(o) = T::GlobalDisputes::determine_voting_winner(market_id) { resolved_outcome_option = Some(o); } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index fb4640a22..af13540d8 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -56,7 +56,6 @@ use zeitgeist_primitives::{ }, }; -#[cfg(feature = "with-global-disputes")] use zeitgeist_primitives::constants::mock::{ GlobalDisputeLockId, GlobalDisputePeriod, GlobalDisputesPalletId, MaxGlobalDisputeVotes, MaxOwners, MinOutcomeVoteAmount, RemoveKeysLimit, VotingOutcomeFee, @@ -86,7 +85,6 @@ parameter_types! { pub const DisputeBond: Balance = 109 * CENT; } -#[cfg(feature = "with-global-disputes")] construct_runtime!( pub enum Runtime where @@ -113,32 +111,6 @@ construct_runtime!( } ); -#[cfg(not(feature = "with-global-disputes"))] -construct_runtime!( - pub enum Runtime - where - Block = BlockTest, - NodeBlock = BlockTest, - UncheckedExtrinsic = UncheckedExtrinsicTest, - { - Authorized: zrml_authorized::{Event, Pallet, Storage}, - Balances: pallet_balances::{Call, Config, Event, Pallet, Storage}, - Court: zrml_court::{Event, Pallet, Storage}, - AssetManager: orml_currencies::{Call, Pallet, Storage}, - LiquidityMining: zrml_liquidity_mining::{Config, Event, Pallet}, - MarketCommons: zrml_market_commons::{Pallet, Storage}, - PredictionMarkets: prediction_markets::{Event, Pallet, Storage}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, - RikiddoSigmoidFeeMarketEma: zrml_rikiddo::{Pallet, Storage}, - SimpleDisputes: zrml_simple_disputes::{Event, Pallet, Storage}, - Swaps: zrml_swaps::{Call, Event, Pallet}, - System: frame_system::{Config, Event, Pallet, Storage}, - Timestamp: pallet_timestamp::{Pallet}, - Tokens: orml_tokens::{Config, Event, Pallet, Storage}, - Treasury: pallet_treasury::{Call, Event, Pallet, Storage}, - } -); - impl crate::Config for Runtime { type AdvisoryBond = AdvisoryBond; type AdvisoryBondSlashPercentage = AdvisoryBondSlashPercentage; @@ -151,9 +123,7 @@ impl crate::Config for Runtime { type DestroyOrigin = EnsureSignedBy; type DisputeBond = DisputeBond; type Event = Event; - #[cfg(feature = "with-global-disputes")] type GlobalDisputes = GlobalDisputes; - #[cfg(feature = "with-global-disputes")] type GlobalDisputePeriod = GlobalDisputePeriod; type LiquidityMining = LiquidityMining; type MaxCategories = MaxCategories; @@ -341,7 +311,6 @@ impl zrml_simple_disputes::Config for Runtime { type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } -#[cfg(feature = "with-global-disputes")] impl zrml_global_disputes::Config for Runtime { type Event = Event; type MarketCommons = MarketCommons; @@ -478,11 +447,13 @@ impl ExtBuilder { pub fn run_to_block(n: BlockNumber) { while System::block_number() < n { Balances::on_finalize(System::block_number()); + Court::on_finalize(System::block_number()); PredictionMarkets::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); System::on_initialize(System::block_number()); PredictionMarkets::on_initialize(System::block_number()); + Court::on_initialize(System::block_number()); Balances::on_initialize(System::block_number()); } } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 8d76e7e49..2a3e10c30 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -19,22 +19,30 @@ #![cfg(all(feature = "mock", test))] #![allow(clippy::reversed_empty_ranges)] +extern crate alloc; + use crate::{ mock::*, Config, Error, Event, LastTimeFrame, MarketIdsForEdit, MarketIdsPerCloseBlock, MarketIdsPerDisputeBlock, MarketIdsPerOpenBlock, MarketIdsPerReportBlock, TimeFrame, }; +use alloc::collections::BTreeMap; use core::ops::{Range, RangeInclusive}; use frame_support::{ assert_err, assert_noop, assert_ok, dispatch::{DispatchError, DispatchResultWithPostInfo}, traits::{NamedReservableCurrency, OnInitialize}, }; +use sp_runtime::traits::BlakeTwo256; use test_case::test_case; +use zrml_court::{types::*, Error as CError}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; -use sp_runtime::traits::{AccountIdConversion, SaturatedConversion, Zero}; +use sp_runtime::traits::{AccountIdConversion, Hash, SaturatedConversion, Zero}; use zeitgeist_primitives::{ - constants::mock::{OutcomeBond, OutcomeFactor, OutsiderBond, BASE, CENT, MILLISECS_PER_BLOCK}, + constants::mock::{ + MaxAppeals, MaxDraws, MinJurorStake, OutcomeBond, OutcomeFactor, OutsiderBond, BASE, CENT, + MILLISECS_PER_BLOCK, + }, traits::Swaps as SwapsPalletApi, types::{ AccountIdTest, Asset, Balance, BlockNumber, Bond, Deadlines, Market, MarketBonds, @@ -2937,6 +2945,295 @@ fn it_resolves_a_disputed_market() { }); } +#[test] +fn it_resolves_a_disputed_court_market() { + let test = |base_asset: Asset| { + let juror_0 = 1000; + let juror_1 = 1001; + let juror_2 = 1002; + let juror_3 = 1003; + let juror_4 = 1004; + let juror_5 = 1005; + + for j in &[juror_0, juror_1, juror_2, juror_3, juror_4, juror_5] { + let amount = MinJurorStake::get() + *j as u128; + assert_ok!(AssetManager::deposit(Asset::Ztg, j, amount + SENTINEL_AMOUNT)); + assert_ok!(Court::join_court(Origin::signed(*j), amount)); + } + + let free_juror_0_before = Balances::free_balance(&juror_0); + let free_juror_1_before = Balances::free_balance(&juror_1); + let free_juror_2_before = Balances::free_balance(&juror_2); + let free_juror_3_before = Balances::free_balance(&juror_3); + let free_juror_4_before = Balances::free_balance(&juror_4); + let free_juror_5_before = Balances::free_balance(&juror_5); + + let end = 2; + assert_ok!(PredictionMarkets::create_market( + Origin::signed(ALICE), + base_asset, + BOB, + MarketPeriod::Block(0..end), + get_deadlines(), + gen_metadata(2), + MarketCreation::Permissionless, + MarketType::Categorical(::MinCategories::get()), + MarketDisputeMechanism::Court, + ScoringRule::CPMM, + )); + + let market_id = 0; + let market = MarketCommons::market(&0).unwrap(); + + let report_at = end + market.deadlines.grace_period + 1; + run_to_block(report_at); + + assert_ok!(PredictionMarkets::report( + Origin::signed(BOB), + market_id, + OutcomeReport::Categorical(0) + )); + + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); + + let court = zrml_court::Courts::::get(market_id).unwrap(); + let vote_start = court.cycle_ends.pre_vote + 1; + + run_to_block(vote_start); + + // overwrite draws to disregard randomness + zrml_court::Draws::::remove(market_id); + let mut draws = zrml_court::Draws::::get(market_id); + for juror in &[juror_0, juror_1, juror_2, juror_3, juror_4, juror_5] { + draws + .try_push(Draw { + juror: *juror, + weight: 1, + vote: Vote::Drawn, + slashable: MinJurorStake::get(), + }) + .unwrap(); + } + let old_draws = draws.clone(); + zrml_court::Draws::::insert(market_id, draws); + + let salt = ::Hash::default(); + + // outcome_0 is the plurality decision => right outcome + let outcome_0 = OutcomeReport::Categorical(0); + // outcome_1 is the wrong outcome + let outcome_1 = OutcomeReport::Categorical(1); + + let commitment_0 = BlakeTwo256::hash_of(&(juror_0, outcome_0.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(juror_0), market_id, commitment_0)); + + // juror_1 votes for non-plurality outcome => slashed later + let commitment_1 = BlakeTwo256::hash_of(&(juror_1, outcome_1.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(juror_1), market_id, commitment_1)); + + let commitment_2 = BlakeTwo256::hash_of(&(juror_2, outcome_0.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(juror_2), market_id, commitment_2)); + + let commitment_3 = BlakeTwo256::hash_of(&(juror_3, outcome_0.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(juror_3), market_id, commitment_3)); + + // juror_4 fails to vote in time + + let commitment_5 = BlakeTwo256::hash_of(&(juror_5, outcome_0.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(juror_5), market_id, commitment_5)); + + // juror_3 is denounced by juror_0 => slashed later + assert_ok!(Court::denounce_vote( + Origin::signed(juror_0), + market_id, + juror_3, + outcome_0.clone(), + salt + )); + + let aggregation_start = court.cycle_ends.vote + 1; + run_to_block(aggregation_start); + + assert_ok!(Court::reveal_vote(Origin::signed(juror_0), market_id, outcome_0.clone(), salt)); + assert_ok!(Court::reveal_vote(Origin::signed(juror_1), market_id, outcome_1, salt)); + + let wrong_salt = BlakeTwo256::hash_of(&69); + assert_noop!( + Court::reveal_vote(Origin::signed(juror_2), market_id, outcome_0.clone(), wrong_salt), + CError::::InvalidReveal + ); + assert_ok!(Court::reveal_vote(Origin::signed(juror_2), market_id, outcome_0.clone(), salt)); + + assert_noop!( + Court::reveal_vote(Origin::signed(juror_3), market_id, outcome_0.clone(), salt), + CError::::VoteAlreadyDenounced + ); + + assert_noop!( + Court::reveal_vote(Origin::signed(juror_4), market_id, outcome_0.clone(), salt), + CError::::JurorNotVoted + ); + + // juror_5 fails to reveal in time + + let resolve_at = court.cycle_ends.appeal; + let market_ids = MarketIdsPerDisputeBlock::::get(resolve_at); + assert_eq!(market_ids.len(), 1); + + run_blocks(resolve_at); + + let market_after = MarketCommons::market(&0).unwrap(); + assert_eq!(market_after.status, MarketStatus::Resolved); + assert_eq!(market_after.resolved_outcome, Some(outcome_0.clone())); + let court_after = zrml_court::Courts::::get(market_id).unwrap(); + assert_eq!(court_after.status, CourtStatus::Closed { winner: outcome_0 }); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(juror_0), market_id)); + + let free_juror_0_after = Balances::free_balance(&juror_0); + let free_juror_1_after = Balances::free_balance(&juror_1); + let free_juror_2_after = Balances::free_balance(&juror_2); + let free_juror_3_after = Balances::free_balance(&juror_3); + let free_juror_4_after = Balances::free_balance(&juror_4); + let free_juror_5_after = Balances::free_balance(&juror_5); + + let mut total_slashed = 0; + // juror_1 voted for the wrong outcome => slashed + assert_eq!(free_juror_1_after, free_juror_1_before - old_draws[1].slashable); + total_slashed += old_draws[1].slashable; + // juror_3 was denounced by juror_0 => slashed + assert_eq!(free_juror_3_after, free_juror_3_before - old_draws[3].slashable); + total_slashed += old_draws[3].slashable; + // juror_4 failed to vote => slashed + assert_eq!(free_juror_4_after, free_juror_4_before - old_draws[4].slashable); + total_slashed += old_draws[4].slashable; + // juror_5 failed to reveal => slashed + assert_eq!(free_juror_5_after, free_juror_5_before - old_draws[5].slashable); + total_slashed += old_draws[5].slashable; + // juror_0 and juror_2 voted for the right outcome => rewarded + assert_eq!(free_juror_0_after, free_juror_0_before + total_slashed / 2); + assert_eq!(free_juror_2_after, free_juror_2_before + total_slashed / 2); + }; + ExtBuilder::default().build().execute_with(|| { + test(Asset::Ztg); + }); + #[cfg(feature = "parachain")] + ExtBuilder::default().build().execute_with(|| { + test(Asset::ForeignAsset(100)); + }); +} + +fn simulate_appeal_cycle(market_id: MarketId) { + let court = zrml_court::Courts::::get(market_id).unwrap(); + let vote_start = court.cycle_ends.pre_vote + 1; + + run_to_block(vote_start); + + let salt = ::Hash::default(); + + let wrong_outcome = OutcomeReport::Categorical(1); + + let draws = zrml_court::Draws::::get(market_id); + for draw in &draws { + let commitment = BlakeTwo256::hash_of(&(draw.juror, wrong_outcome.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(draw.juror), market_id, commitment)); + } + + let aggregation_start = court.cycle_ends.vote + 1; + run_to_block(aggregation_start); + + for draw in draws { + assert_ok!(Court::reveal_vote( + Origin::signed(draw.juror), + market_id, + wrong_outcome.clone(), + salt, + )); + } + + let resolve_at = court.cycle_ends.appeal; + let market_ids = MarketIdsPerDisputeBlock::::get(resolve_at); + assert_eq!(market_ids.len(), 1); + + run_to_block(resolve_at - 1); + + let market_after = MarketCommons::market(&0).unwrap(); + assert_eq!(market_after.status, MarketStatus::Disputed); +} + +#[test] +fn it_appeals_a_court_market_to_global_dispute() { + let test = |base_asset: Asset| { + let mut free_before = BTreeMap::new(); + let jurors = 1000..(1000 + MaxDraws::get() as u128); + for j in jurors { + let amount = MinJurorStake::get() + j as u128; + assert_ok!(AssetManager::deposit(Asset::Ztg, &j, amount + SENTINEL_AMOUNT)); + assert_ok!(Court::join_court(Origin::signed(j), amount)); + free_before.insert(j, Balances::free_balance(&j)); + } + + let end = 2; + assert_ok!(PredictionMarkets::create_market( + Origin::signed(ALICE), + base_asset, + BOB, + MarketPeriod::Block(0..end), + get_deadlines(), + gen_metadata(2), + MarketCreation::Permissionless, + MarketType::Categorical(::MinCategories::get()), + MarketDisputeMechanism::Court, + ScoringRule::CPMM, + )); + + let market_id = 0; + let market = MarketCommons::market(&0).unwrap(); + + let report_at = end + market.deadlines.grace_period + 1; + run_to_block(report_at); + + assert_ok!(PredictionMarkets::report( + Origin::signed(BOB), + market_id, + OutcomeReport::Categorical(0) + )); + + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); + + for _ in 0..(MaxAppeals::get() - 1) { + simulate_appeal_cycle(market_id); + assert_ok!(Court::appeal(Origin::signed(BOB), market_id)); + } + + let court = zrml_court::Courts::::get(market_id).unwrap(); + let appeals = court.appeals; + assert_eq!(appeals.len(), (MaxAppeals::get() - 1) as usize); + + assert_noop!( + PredictionMarkets::start_global_dispute(Origin::signed(BOB), market_id), + Error::::MarketDisputeMechanismNotFailed + ); + + simulate_appeal_cycle(market_id); + assert_ok!(Court::appeal(Origin::signed(BOB), market_id)); + + assert_noop!( + Court::appeal(Origin::signed(BOB), market_id), + CError::::MaxAppealsReached + ); + + assert_ok!(PredictionMarkets::start_global_dispute(Origin::signed(BOB), market_id)); + }; + ExtBuilder::default().build().execute_with(|| { + test(Asset::Ztg); + }); + #[cfg(feature = "parachain")] + ExtBuilder::default().build().execute_with(|| { + test(Asset::ForeignAsset(100)); + }); +} + #[test_case(MarketStatus::Active; "active")] #[test_case(MarketStatus::CollectingSubsidy; "collecting_subsidy")] #[test_case(MarketStatus::InsufficientSubsidy; "insufficient_subsidy")] @@ -2995,7 +3292,6 @@ fn start_global_dispute_works() { run_to_block(dispute_at_0); assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); for i in 1..=::MaxDisputes::get() { - #[cfg(feature = "with-global-disputes")] assert_noop!( PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), Error::::MarketDisputeMechanismNotFailed @@ -3019,46 +3315,43 @@ fn start_global_dispute_works() { let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); assert_eq!(removable_market_ids.len(), 1); - #[cfg(feature = "with-global-disputes")] - { - use zrml_global_disputes::GlobalDisputesPalletApi; + use zrml_global_disputes::GlobalDisputesPalletApi; - let now = >::block_number(); - assert_ok!(PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id)); + let now = >::block_number(); + assert_ok!(PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id)); - // report check + // report check + assert_eq!( + GlobalDisputes::get_voting_outcome_info(&market_id, &OutcomeReport::Categorical(0)), + Some((Zero::zero(), vec![BOB])), + ); + for i in 1..=::MaxDisputes::get() { + let dispute_bond = + zrml_simple_disputes::default_outcome_bond::((i - 1).into()); assert_eq!( - GlobalDisputes::get_voting_outcome_info(&market_id, &OutcomeReport::Categorical(0)), - Some((Zero::zero(), vec![BOB])), + GlobalDisputes::get_voting_outcome_info( + &market_id, + &OutcomeReport::Categorical(i.saturated_into()) + ), + Some((dispute_bond, vec![CHARLIE])), ); - for i in 1..=::MaxDisputes::get() { - let dispute_bond = - zrml_simple_disputes::default_outcome_bond::((i - 1).into()); - assert_eq!( - GlobalDisputes::get_voting_outcome_info( - &market_id, - &OutcomeReport::Categorical(i.saturated_into()) - ), - Some((dispute_bond, vec![CHARLIE])), - ); - } + } - // remove_last_dispute_from_market_ids_per_dispute_block works - let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); - assert_eq!(removable_market_ids.len(), 0); + // remove_last_dispute_from_market_ids_per_dispute_block works + let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); + assert_eq!(removable_market_ids.len(), 0); - let market_ids = MarketIdsPerDisputeBlock::::get( - now + ::GlobalDisputePeriod::get(), - ); - assert_eq!(market_ids, vec![market_id]); - assert!(GlobalDisputes::is_started(&market_id)); - System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); + let market_ids = MarketIdsPerDisputeBlock::::get( + now + ::GlobalDisputePeriod::get(), + ); + assert_eq!(market_ids, vec![market_id]); + assert!(GlobalDisputes::is_started(&market_id)); + System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); - assert_noop!( - PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), - Error::::GlobalDisputeAlreadyStarted - ); - } + assert_noop!( + PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), + Error::::GlobalDisputeAlreadyStarted + ); }); } @@ -3097,7 +3390,6 @@ fn start_global_dispute_fails_on_wrong_mdm() { let market = MarketCommons::market(&market_id).unwrap(); assert_eq!(market.status, MarketStatus::Disputed); - #[cfg(feature = "with-global-disputes")] assert_noop!( PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), Error::::InvalidDisputeMechanism @@ -3105,25 +3397,6 @@ fn start_global_dispute_fails_on_wrong_mdm() { }); } -#[test] -fn start_global_dispute_works_without_feature() { - ExtBuilder::default().build().execute_with(|| { - let non_market_id = 0; - - #[cfg(not(feature = "with-global-disputes"))] - assert_noop!( - PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), non_market_id), - Error::::GlobalDisputesDisabled - ); - - #[cfg(feature = "with-global-disputes")] - assert_noop!( - PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), non_market_id), - zrml_market_commons::Error::::MarketDoesNotExist - ); - }); -} - #[test] fn it_allows_to_redeem_shares() { let test = |base_asset: Asset| { From 499d1fb16e884096264347449485ca17426e867c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 6 Apr 2023 12:49:07 +0200 Subject: [PATCH 233/368] remove invalid test --- zrml/court/src/tests.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index f67bcb04b..7d9a490d4 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -2050,14 +2050,6 @@ fn get_auto_resolve_fails_if_wrong_dispute_mechanism() { }); } -#[test] -fn get_auto_resolve_fails_if_court_not_found() { - ExtBuilder::default().build().execute_with(|| { - let market = DEFAULT_MARKET; - assert_noop!(Court::get_auto_resolve(&0, &market), Error::::CourtNotFound); - }); -} - #[test] fn on_global_dispute_removes_court() { ExtBuilder::default().build().execute_with(|| { From bc635e5e6bab6b04a6e71cd13d9764e005c7f3fc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 6 Apr 2023 14:41:13 +0200 Subject: [PATCH 234/368] add missing WeightInfo --- zrml/court/src/lib.rs | 104 ++++++++++++++++++++++++++------------ zrml/court/src/weights.rs | 13 ----- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index acd6ada3f..737e02c7e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -49,7 +49,10 @@ mod pallet { use frame_support::{ dispatch::DispatchResult, ensure, log, - pallet_prelude::{Hooks, OptionQuery, StorageMap, StorageValue, ValueQuery, Weight}, + pallet_prelude::{ + ConstU32, DispatchResultWithPostInfo, Hooks, OptionQuery, StorageMap, StorageValue, + ValueQuery, Weight, + }, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, NamedReservableCurrency, OnUnbalanced, Randomness, ReservableCurrency, StorageVersion, @@ -193,6 +196,7 @@ mod pallet { CommitmentMatcher, ::Hash>; pub(crate) type RawCommitmentOf = RawCommitment, ::Hash>; + pub type CacheSize = ConstU32<64>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -366,9 +370,12 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight(T::WeightInfo::join_court(T::MaxJurors::get()))] #[transactional] - pub fn join_court(origin: OriginFor, amount: BalanceOf) -> DispatchResult { + pub fn join_court( + origin: OriginFor, + amount: BalanceOf, + ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); let free_balance = T::Currency::free_balance(&who); @@ -434,13 +441,15 @@ mod pallet { T::Currency::set_lock(T::CourtLockId::get(), &who, amount, WithdrawReasons::all()); + let jurors_len = jurors.len() as u32; JurorPool::::put(jurors); let juror_info = JurorInfoOf:: { stake: amount, active_lock }; >::insert(&who, juror_info); Self::deposit_event(Event::JurorJoined { juror: who, stake: amount }); - Ok(()) + + Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) } /// Prepare as a juror to exit the court. @@ -452,15 +461,15 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight(T::WeightInfo::prepare_exit_court(T::MaxJurors::get()))] #[transactional] - pub fn prepare_exit_court(origin: OriginFor) -> DispatchResult { + pub fn prepare_exit_court(origin: OriginFor) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExist)?; let mut jurors = JurorPool::::get(); - + let jurors_len = jurors.len() as u32; if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) { jurors.remove(index); >::put(jurors); @@ -471,7 +480,8 @@ mod pallet { } Self::deposit_event(Event::JurorPreparedExit { juror: who }); - Ok(()) + + Ok(Some(T::WeightInfo::prepare_exit_court(jurors_len)).into()) } /// Exit the court. @@ -486,25 +496,36 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight( + T::WeightInfo::exit_court_set(T::MaxJurors::get()) + .max(T::WeightInfo::exit_court_remove(T::MaxJurors::get())) + )] #[transactional] - pub fn exit_court(origin: OriginFor, juror: AccountIdLookupOf) -> DispatchResult { + pub fn exit_court( + origin: OriginFor, + juror: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; let juror = T::Lookup::lookup(juror)?; let mut prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; + let jurors = JurorPool::::get(); + let jurors_len = jurors.len() as u32; ensure!( - Self::get_pool_item(&JurorPool::::get(), prev_juror_info.stake, &juror) - .is_none(), + Self::get_pool_item(&jurors, prev_juror_info.stake, &juror).is_none(), Error::::JurorNotPreparedToExit ); - let (exit_amount, active_lock) = if prev_juror_info.active_lock.is_zero() { + let (exit_amount, active_lock, weight) = if prev_juror_info.active_lock.is_zero() { T::Currency::remove_lock(T::CourtLockId::get(), &juror); Jurors::::remove(&juror); - (prev_juror_info.stake, >::zero()) + ( + prev_juror_info.stake, + >::zero(), + T::WeightInfo::exit_court_remove(jurors_len), + ) } else { let active_lock = prev_juror_info.active_lock; let exit_amount = prev_juror_info.stake.saturating_sub(active_lock); @@ -518,12 +539,12 @@ mod pallet { prev_juror_info.stake = active_lock; Jurors::::insert(&juror, prev_juror_info); - (exit_amount, active_lock) + (exit_amount, active_lock, T::WeightInfo::exit_court_set(jurors_len)) }; Self::deposit_event(Event::JurorExited { juror, exit_amount, active_lock }); - Ok(()) + Ok(Some(weight).into()) } /// Vote as a randomly selected juror for a specific court case. @@ -537,13 +558,13 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors /// in the list of random selections (draws). - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight(T::WeightInfo::vote(T::MaxDraws::get()))] #[transactional] pub fn vote( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, commitment_vote: T::Hash, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -569,6 +590,8 @@ mod pallet { let vote = Vote::Secret { commitment: commitment_vote }; draws[index] = Draw { juror: who.clone(), vote, ..draw }; + let draws_len = draws.len() as u32; + >::insert(market_id, draws); Self::deposit_event(Event::JurorVoted { @@ -576,7 +599,8 @@ mod pallet { market_id, commitment: commitment_vote, }); - Ok(()) + + Ok(Some(T::WeightInfo::vote(draws_len)).into()) } /// Denounce a juror during the voting period for which the commitment vote is known. @@ -596,7 +620,7 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors /// in the list of random selections (draws). - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight(T::WeightInfo::denounce_vote(T::MaxDraws::get()))] #[transactional] pub fn denounce_vote( origin: OriginFor, @@ -604,7 +628,7 @@ mod pallet { juror: AccountIdLookupOf, outcome: OutcomeReport, salt: T::Hash, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let denouncer = ensure_signed(origin)?; let market = T::MarketCommons::market(&market_id)?; @@ -625,6 +649,7 @@ mod pallet { ); let mut draws = >::get(market_id); + let draws_len = draws.len() as u32; let (index, draw) = match draws.iter().position(|draw| draw.juror == juror) { Some(index) => (index, draws[index].clone()), None => return Err(Error::::JurorNotDrawn.into()), @@ -647,7 +672,8 @@ mod pallet { outcome, salt, }); - Ok(()) + + Ok(Some(T::WeightInfo::denounce_vote(draws_len)).into()) } /// Reveal the commitment vote of the caller juror. @@ -663,14 +689,14 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors /// in the list of random selections (draws). - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight(T::WeightInfo::reveal_vote(T::MaxDraws::get()))] #[transactional] pub fn reveal_vote( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, outcome: OutcomeReport, salt: T::Hash, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; let market = T::MarketCommons::market(&market_id)?; @@ -685,6 +711,7 @@ mod pallet { ); let mut draws = >::get(market_id); + let draws_len = draws.len() as u32; let (index, draw) = match draws.iter().position(|draw| draw.juror == who) { Some(index) => (index, draws[index].clone()), None => return Err(Error::::JurorNotDrawn.into()), @@ -700,7 +727,8 @@ mod pallet { >::insert(market_id, draws); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); - Ok(()) + + Ok(Some(T::WeightInfo::reveal_vote(draws_len)).into()) } /// Trigger an appeal for a court. The last appeal does not trigger a new court round @@ -714,9 +742,16 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors. /// It depends heavily on `choose_multiple_weighted` of `select_jurors`. - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight(T::WeightInfo::appeal( + T::MaxJurors::get(), + T::MaxAppeals::get(), + CacheSize::get(), + ))] #[transactional] - pub fn appeal(origin: OriginFor, market_id: MarketIdOf) -> DispatchResult { + pub fn appeal( + origin: OriginFor, + market_id: MarketIdOf, + ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -739,6 +774,9 @@ mod pallet { let last_resolve_at = court.cycle_ends.appeal; + // used for benchmarking, juror pool is queried inside `select_jurors` + let jurors_len = >::decode_len().unwrap_or(0) as u32; + // if appeal_number == MaxAppeals, then don't start a new appeal round if appeal_number < T::MaxAppeals::get() as usize { let new_draws = Self::select_jurors(appeal_number)?; @@ -754,13 +792,14 @@ mod pallet { court.update_lifecycle(round_timing); let new_resolve_at = court.cycle_ends.appeal; debug_assert!(new_resolve_at != last_resolve_at); + // TODO benchmark component missing let _ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; >::insert(market_id, new_draws); Self::unlock_jurors_from_last_draw(&market_id, old_draws); } - let _ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + let ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; @@ -769,7 +808,7 @@ mod pallet { let appeal_number = appeal_number as u32; Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); - Ok(()) + Ok(Some(T::WeightInfo::appeal(jurors_len, appeal_number, ids_len_0)).into()) } /// The juror stakes get reassigned according to the plurality decision of the jurors. @@ -783,12 +822,12 @@ mod pallet { /// # Weight /// /// Complexity: `O(n)`, where `n` is the number of randomly selected jurors for this court. - #[pallet::weight(1_000_000_000_000)] + #[pallet::weight(T::WeightInfo::reassign_juror_stakes(T::MaxDraws::get()))] #[transactional] pub fn reassign_juror_stakes( origin: OriginFor, market_id: MarketIdOf, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -799,6 +838,7 @@ mod pallet { }; let draws = Draws::::get(market_id); + let draws_len = draws.len() as u32; let reward_pot = Self::reward_pot(&market_id); let slash_juror = |ai: &T::AccountId, slashable: BalanceOf| { @@ -852,7 +892,7 @@ mod pallet { Self::deposit_event(Event::JurorStakesReassigned { market_id }); - Ok(()) + Ok(Some(T::WeightInfo::reassign_juror_stakes(draws_len)).into()) } } diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 402b27fba..215549b41 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -53,7 +53,6 @@ pub trait WeightInfoZeitgeist { fn denounce_vote(d: u32) -> Weight; fn reveal_vote(d: u32) -> Weight; fn appeal(d: u32, a: u32, r: u32) -> Weight; - fn punish_tardy_jurors(d: u32) -> Weight; fn reassign_juror_stakes(d: u32) -> Weight; } @@ -151,18 +150,6 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().writes((28_u64).saturating_mul(a.into()))) } // Storage: Court Courts (r:1 w:1) - // Storage: Court Draws (r:1 w:0) - // Storage: System Account (r:2 w:2) - fn punish_tardy_jurors(d: u32) -> Weight { - Weight::from_ref_time(42_725_000) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(15_689_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) - .saturating_add(T::DbWeight::get().writes(2)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) - } - // Storage: Court Courts (r:1 w:1) // Storage: Court Draws (r:1 w:1) // Storage: Court Jurors (r:1 w:1) // Storage: System Account (r:1 w:0) From 419c89790cdf73ce5e8423e03303f7d1b0b50bf3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 11 Apr 2023 16:30:51 +0200 Subject: [PATCH 235/368] add inflation inside court --- Cargo.lock | 1 + primitives/src/constants.rs | 8 +- primitives/src/constants/mock.rs | 2 + runtime/battery-station/src/parameters.rs | 4 + runtime/common/src/lib.rs | 2 + runtime/zeitgeist/src/parameters.rs | 4 + zrml/court/Cargo.toml | 1 + zrml/court/src/benchmarks.rs | 16 ++- zrml/court/src/lib.rs | 158 +++++++++++++++++++--- zrml/court/src/mock.rs | 10 +- zrml/court/src/tests.rs | 72 +++++++--- zrml/court/src/types.rs | 4 +- 12 files changed, 239 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 415fea58c..86226eb2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12920,6 +12920,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "scale-info", + "sp-arithmetic", "sp-io", "sp-runtime", "test-case", diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs index f528cfdac..606e352e6 100644 --- a/primitives/src/constants.rs +++ b/primitives/src/constants.rs @@ -30,11 +30,11 @@ use crate::types::{Balance, BlockNumber}; use frame_support::{parameter_types, PalletId}; // Definitions for time -pub const BLOCKS_PER_YEAR: BlockNumber = (BLOCKS_PER_DAY * 36525) / 100; -pub const BLOCKS_PER_DAY: BlockNumber = BLOCKS_PER_HOUR * 24; +pub const BLOCKS_PER_YEAR: BlockNumber = (BLOCKS_PER_DAY * 36525) / 100; // 2_629_800 +pub const BLOCKS_PER_DAY: BlockNumber = BLOCKS_PER_HOUR * 24; // 7_200 pub const MILLISECS_PER_BLOCK: u32 = 12000; -pub const BLOCKS_PER_MINUTE: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); -pub const BLOCKS_PER_HOUR: BlockNumber = BLOCKS_PER_MINUTE * 60; +pub const BLOCKS_PER_MINUTE: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); // 5 +pub const BLOCKS_PER_HOUR: BlockNumber = BLOCKS_PER_MINUTE * 60; // 300 // Definitions for currency pub const BASE: u128 = 10_000_000_000; diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 5e77b7b8d..ed198c593 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -36,6 +36,7 @@ parameter_types! { parameter_types! { pub const AppealBond: Balance = 5 * BASE; pub const AppealBondFactor: Balance = 2 * BASE; + pub const BlocksPerYear: BlockNumber = 100; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); pub const RequestInterval: BlockNumber = 15; pub const CourtVotePeriod: BlockNumber = 3; @@ -46,6 +47,7 @@ parameter_types! { pub const MaxDraws: u32 = 47; pub const MaxJurors: u32 = 1_000; pub const MinJurorStake: Balance = 50 * CENT; + pub const InflationPeriod: BlockNumber = 20; } // Global disputes parameters diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 6d461069a..58205a37b 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -81,6 +81,8 @@ parameter_types! { /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` /// was deposited. pub const AppealBond: Balance = 5 * BASE; + /// The blocks per year required to calculate the yearly inflation for court incentivisation. + pub const BlocksPerYear: BlockNumber = BLOCKS_PER_YEAR; /// Pallet identifier, mainly used for named balance reserves. pub const CourtPalletId: PalletId = COURT_PALLET_ID; /// The time in which the jurors can cast their secret vote. @@ -91,6 +93,8 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = BLOCKS_PER_DAY; /// The court lock identifier. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; + /// The time in which the inflation is periodically issued. + pub const InflationPeriod: BlockNumber = 30 * BLOCKS_PER_DAY; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 4; /// The maximum number of randomly selected jurors for a dispute. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 846d03284..75c6d9e36 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -947,6 +947,7 @@ macro_rules! impl_config_traits { impl zrml_court::Config for Runtime { type AppealBond = AppealBond; + type BlocksPerYear = BlocksPerYear; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; @@ -955,6 +956,7 @@ macro_rules! impl_config_traits { type Currency = Balances; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; + type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 317caa09a..18fc09b3f 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -81,6 +81,8 @@ parameter_types! { /// Slashed in case the final outcome does match the appealed outcome for which the `AppealBond` /// was deposited. pub const AppealBond: Balance = 2000 * BASE; + /// The blocks per year required to calculate the yearly inflation for court incentivisation. + pub const BlocksPerYear: BlockNumber = BLOCKS_PER_YEAR; /// Pallet identifier, mainly used for named balance reserves. DO NOT CHANGE. pub const CourtPalletId: PalletId = COURT_PALLET_ID; /// The time in which the jurors can cast their secret vote. @@ -91,6 +93,8 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = BLOCKS_PER_DAY; /// The lock identifier for the court votes. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; + /// The time in which the inflation is periodically issued. + pub const InflationPeriod: BlockNumber = 30 * BLOCKS_PER_DAY; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 4; /// The maximum number of randomly selected jurors for a dispute. diff --git a/zrml/court/Cargo.toml b/zrml/court/Cargo.toml index 094556156..5c3877488 100644 --- a/zrml/court/Cargo.toml +++ b/zrml/court/Cargo.toml @@ -7,6 +7,7 @@ parity-scale-codec = { default-features = false, features = ["derive", "max-enco rand = { default-features = false, features = ["alloc", "std_rng"], version = "0.8" } rand_chacha = { default-features = false, version = "0.3.1" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-arithmetic = { branch = "moonbeam-polkadot-v0.9.29", default-features = false, git = "https://github.com/zeitgeistpm/substrate" } sp-runtime = { branch = "moonbeam-polkadot-v0.9.29", default-features = false, git = "https://github.com/zeitgeistpm/substrate" } zeitgeist-primitives = { default-features = false, path = "../../primitives" } zrml-market-commons = { default-features = false, path = "../market-commons" } diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index a608fd5f9..f29f11f6b 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -99,7 +99,7 @@ where let stake = max_amount - BalanceOf::::from(i); >::insert( juror.clone(), - JurorInfo { stake, active_lock: >::zero() }, + JurorInfo { stake, active_lock: >::zero(), prepare_exit_at: None }, ); let consumed_stake = BalanceOf::::zero(); let pool_item = JurorPoolItem { stake, juror: juror.clone(), consumed_stake }; @@ -151,7 +151,11 @@ where deposit::(&juror); >::insert( &juror, - JurorInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get() }, + JurorInfo { + stake: T::MinJurorStake::get(), + active_lock: T::MinJurorStake::get(), + prepare_exit_at: None, + }, ); let draw = Draw { juror, vote: Vote::Drawn, weight: 1u32, slashable: T::MinJurorStake::get() }; @@ -192,6 +196,8 @@ benchmarks! { join_with_min_stake::(&caller)?; Court::::prepare_exit_court(RawOrigin::Signed(caller.clone()).into())?; + let now = >::block_number(); + >::set_block_number(now + T::InflationPeriod::get()); >::mutate(caller.clone(), |prev_juror_info| { prev_juror_info.as_mut().unwrap().active_lock = >::zero(); @@ -209,6 +215,8 @@ benchmarks! { join_with_min_stake::(&caller)?; Court::::prepare_exit_court(RawOrigin::Signed(caller.clone()).into())?; + let now = >::block_number(); + >::set_block_number(now + T::InflationPeriod::get()); >::mutate(caller.clone(), |prev_juror_info| { prev_juror_info.as_mut().unwrap().active_lock = T::MinJurorStake::get(); @@ -266,6 +274,7 @@ benchmarks! { >::insert(&denounced_juror, JurorInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), + prepare_exit_at: None, }); let denounced_juror_unlookup = T::Lookup::unlookup(denounced_juror.clone()); let commitment = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); @@ -302,6 +311,7 @@ benchmarks! { >::insert(&caller, JurorInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), + prepare_exit_at: None, }); let commitment = T::Hashing::hash_of(&(caller.clone(), outcome.clone(), salt)); @@ -362,6 +372,7 @@ benchmarks! { >::insert(&juror, JurorInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), + prepare_exit_at: None, }); let outcome = OutcomeReport::Scalar(i as u128); let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); @@ -417,6 +428,7 @@ benchmarks! { >::insert(&juror, JurorInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), + prepare_exit_at: None, }); let outcome = winner_outcome.clone(); let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 737e02c7e..1c0c1110d 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -50,8 +50,8 @@ mod pallet { dispatch::DispatchResult, ensure, log, pallet_prelude::{ - ConstU32, DispatchResultWithPostInfo, Hooks, OptionQuery, StorageMap, StorageValue, - ValueQuery, Weight, + ConstU32, DispatchResultWithPostInfo, EnsureOrigin, Hooks, OptionQuery, StorageMap, + StorageValue, ValueQuery, Weight, }, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, @@ -60,12 +60,16 @@ mod pallet { }, transactional, Blake2_128Concat, BoundedVec, PalletId, }; - use frame_system::{ensure_signed, pallet_prelude::OriginFor}; + use frame_system::{ + ensure_signed, + pallet_prelude::{BlockNumberFor, OriginFor}, + }; use rand::{Rng, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; + use sp_arithmetic::{per_things::Perquintill, traits::One}; use sp_runtime::{ traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup, Zero}, - DispatchError, SaturatedConversion, + DispatchError, Perbill, SaturatedConversion, }; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, @@ -80,6 +84,9 @@ mod pallet { #[pallet::constant] type AppealBond: Get>; + #[pallet::constant] + type BlocksPerYear: Get; + /// The time in which the jurors can cast their commitment vote. #[pallet::constant] type CourtVotePeriod: Get; @@ -116,6 +123,9 @@ mod pallet { /// Event type Event: From> + IsType<::Event>; + #[pallet::constant] + type InflationPeriod: Get; + /// Market commons type MarketCommons: MarketCommonsPalletApi< AccountId = Self::AccountId, @@ -139,6 +149,9 @@ mod pallet { #[pallet::constant] type MinJurorStake: Get>; + /// The origin for monetary governance + type MonetaryGovernanceOrigin: EnsureOrigin; + /// Randomness source type Random: Randomness; @@ -184,7 +197,7 @@ mod pallet { pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; - pub(crate) type JurorInfoOf = JurorInfo>; + pub(crate) type JurorInfoOf = JurorInfo, BlockNumberFor>; pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; pub(crate) type DrawOf = @@ -231,6 +244,15 @@ mod pallet { #[pallet::storage] pub type RequestBlock = StorageValue<_, T::BlockNumber, ValueQuery>; + #[pallet::type_value] + pub fn DefaultInflation() -> Perbill { + Perbill::from_perthousand(20u32) + } + + /// The current inflation rate. + #[pallet::storage] + pub type YearlyInflation = StorageValue<_, Perbill, ValueQuery, DefaultInflation>; + #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event @@ -238,13 +260,26 @@ mod pallet { T: Config, { /// A juror has been added to the court. - JurorJoined { juror: T::AccountId, stake: BalanceOf }, + JurorJoined { + juror: T::AccountId, + stake: BalanceOf, + }, /// A juror prepared to exit the court. - JurorPreparedExit { juror: T::AccountId }, + JurorPreparedExit { + juror: T::AccountId, + }, /// A juror has been removed from the court. - JurorExited { juror: T::AccountId, exit_amount: BalanceOf, active_lock: BalanceOf }, + JurorExited { + juror: T::AccountId, + exit_amount: BalanceOf, + active_lock: BalanceOf, + }, /// A juror has voted in a court. - JurorVoted { market_id: MarketIdOf, juror: T::AccountId, commitment: T::Hash }, + JurorVoted { + market_id: MarketIdOf, + juror: T::AccountId, + commitment: T::Hash, + }, /// A juror has revealed their vote. JurorRevealedVote { juror: T::AccountId, @@ -261,12 +296,21 @@ mod pallet { salt: T::Hash, }, /// A market has been appealed. - MarketAppealed { market_id: MarketIdOf, appeal_number: u32 }, + MarketAppealed { + market_id: MarketIdOf, + appeal_number: u32, + }, + MintedInCourt { + juror: T::AccountId, + amount: BalanceOf, + }, /// The juror stakes have been reassigned. The losing jurors have been slashed. /// The winning jurors have been rewarded by the losers. /// The losing jurors are those, who did not vote, /// were denounced or did not reveal their vote. - JurorStakesReassigned { market_id: MarketIdOf }, + JurorStakesReassigned { + market_id: MarketIdOf, + }, } #[pallet::error] @@ -340,12 +384,18 @@ mod pallet { AppealBondExceedsBalance, /// The outcome does not match the market outcome type. OutcomeMismatch, + /// The juror should at least wait one inflation period after the funds can be unstaked. + /// Otherwise hopping in and out for inflation rewards is possible. + WaitFullInflationPeriod, + /// The `prepare_exit_at` field is not present. + PrepareExitAtNotPresent, } #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(now: T::BlockNumber) -> Weight { let mut total_weight: Weight = Weight::zero(); + total_weight = total_weight.saturating_add(Self::handle_inflation(now)); if now >= >::get() { let future_request = now.saturating_add(T::RequestInterval::get()); >::put(future_request); @@ -389,13 +439,17 @@ mod pallet { ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) .ok_or(Error::::JurorNeedsToExit)?; + debug_assert!( + prev_juror_info.prepare_exit_at.is_none(), + "If the pool item is found, the prepare_exit_at could have never been written." + ); let consumed_stake = pool_item.consumed_stake; jurors.remove(index); (prev_juror_info.active_lock, consumed_stake) } else { if jurors.is_full() { - let lowest_juror = jurors - .first() + let lowest_item = jurors.first(); + let lowest_stake = lowest_item .map(|pool_item| pool_item.stake) .unwrap_or_else(>::zero); debug_assert!({ @@ -405,9 +459,16 @@ mod pallet { && jurors .iter() .zip(sorted.iter()) - .all(|(a, b)| lowest_juror <= a.stake && a == b) + .all(|(a, b)| lowest_stake <= a.stake && a == b) + }); + ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); + lowest_item.map(|pool_item| { + if let Some(mut lowest_juror_info) = >::get(&pool_item.juror) { + let now = >::block_number(); + lowest_juror_info.prepare_exit_at = Some(now); + >::insert(&pool_item.juror, lowest_juror_info); + } }); - ensure!(amount > lowest_juror, Error::::AmountBelowLowestJuror); // remove the lowest staked juror jurors.remove(0); } @@ -444,7 +505,7 @@ mod pallet { let jurors_len = jurors.len() as u32; JurorPool::::put(jurors); - let juror_info = JurorInfoOf:: { stake: amount, active_lock }; + let juror_info = JurorInfoOf:: { stake: amount, active_lock, prepare_exit_at: None }; >::insert(&who, juror_info); Self::deposit_event(Event::JurorJoined { juror: who, stake: amount }); @@ -466,7 +527,8 @@ mod pallet { pub fn prepare_exit_court(origin: OriginFor) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExist)?; + let mut prev_juror_info = + >::get(&who).ok_or(Error::::JurorDoesNotExist)?; let mut jurors = JurorPool::::get(); let jurors_len = jurors.len() as u32; @@ -479,6 +541,10 @@ mod pallet { return Err(Error::::JurorAlreadyPreparedToExit.into()); } + let now = >::block_number(); + prev_juror_info.prepare_exit_at = Some(now); + >::insert(&who, prev_juror_info); + Self::deposit_event(Event::JurorPreparedExit { juror: who }); Ok(Some(T::WeightInfo::prepare_exit_court(jurors_len)).into()) @@ -511,6 +577,7 @@ mod pallet { let mut prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; + let jurors = JurorPool::::get(); let jurors_len = jurors.len() as u32; ensure!( @@ -518,6 +585,14 @@ mod pallet { Error::::JurorNotPreparedToExit ); + let now = >::block_number(); + let prepare_exit_at = + prev_juror_info.prepare_exit_at.ok_or(Error::::PrepareExitAtNotPresent)?; + ensure!( + now.saturating_sub(prepare_exit_at) >= T::InflationPeriod::get(), + Error::::WaitFullInflationPeriod + ); + let (exit_amount, active_lock, weight) = if prev_juror_info.active_lock.is_zero() { T::Currency::remove_lock(T::CourtLockId::get(), &juror); Jurors::::remove(&juror); @@ -894,12 +969,61 @@ mod pallet { Ok(Some(T::WeightInfo::reassign_juror_stakes(draws_len)).into()) } + + // TODO + #[pallet::weight(5000)] + #[transactional] + pub fn set_inflation(origin: OriginFor, inflation: Perbill) -> DispatchResult { + T::MonetaryGovernanceOrigin::ensure_origin(origin)?; + + >::put(inflation); + + Ok(()) + } } impl Pallet where T: Config, { + // Handle the external incentivisation of the court system. + pub(crate) fn handle_inflation(now: T::BlockNumber) -> Weight { + let inflation_period = T::InflationPeriod::get(); + if (now % inflation_period).is_zero() { + let yearly_inflation_rate = >::get(); + let yearly_inflation_amount = yearly_inflation_rate * T::Currency::total_issuance(); + let blocks_per_year = T::BlocksPerYear::get() + .saturated_into::() + .saturated_into::>(); + let issue_per_block = yearly_inflation_amount / blocks_per_year.max(One::one()); + + let inflation_period_mint = issue_per_block.saturating_mul( + inflation_period.saturated_into::().saturated_into::>(), + ); + + let jurors = >::get(); + let total_stake = jurors.iter().fold(0u128, |acc, pool_item| { + acc.saturating_add(pool_item.stake.saturated_into::()) + }); + for JurorPoolItem { stake, juror, .. } in jurors { + let share = + Perquintill::from_rational(stake.saturated_into::(), total_stake); + let mint = share * inflation_period_mint.saturated_into::(); + if let Ok(imb) = T::Currency::deposit_into_existing( + &juror, + mint.saturated_into::>(), + ) { + Self::deposit_event(Event::MintedInCourt { + juror: juror.clone(), + amount: imb.peek(), + }); + } + } + } + // TODO: add weight + Weight::zero() + } + // Get `n` unique and ordered random numbers from the random number generator. // If the generator returns three times the same number in a row, an error is returned. pub(crate) fn get_n_random_numbers( diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index aa813a7c6..f36f1b267 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -33,9 +33,10 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - AppealBond, BlockHashCount, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, - CourtPalletId, CourtVotePeriod, MaxAppeals, MaxApprovals, MaxDraws, MaxJurors, MaxReserves, - MinJurorStake, MinimumPeriod, PmPalletId, RequestInterval, BASE, + AppealBond, BlockHashCount, BlocksPerYear, CourtAggregationPeriod, CourtAppealPeriod, + CourtLockId, CourtPalletId, CourtVotePeriod, InflationPeriod, MaxAppeals, MaxApprovals, + MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, + RequestInterval, BASE, }, traits::DisputeResolutionApi, types::{ @@ -138,6 +139,7 @@ impl DisputeResolutionApi for MockResolution { impl crate::Config for Runtime { type AppealBond = AppealBond; + type BlocksPerYear = BlocksPerYear; type CourtLockId = CourtLockId; type Currency = Balances; type CourtVotePeriod = CourtVotePeriod; @@ -145,11 +147,13 @@ impl crate::Config for Runtime { type CourtAppealPeriod = CourtAppealPeriod; type DisputeResolution = MockResolution; type Event = Event; + type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; + type MonetaryGovernanceOrigin = EnsureSignedBy; type CourtPalletId = CourtPalletId; type Random = MockStorage; type RequestInterval = RequestInterval; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 7d9a490d4..10113e0b8 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -43,7 +43,7 @@ use zeitgeist_primitives::{ constants::{ mock::{ AppealBond, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, - MaxAppeals, MaxJurors, MinJurorStake, RequestInterval, + InflationPeriod, MaxAppeals, MaxJurors, MinJurorStake, RequestInterval, }, BASE, }, @@ -134,7 +134,10 @@ fn put_alice_in_draw(market_id: MarketIdOf, stake: BalanceOf) let slashable = MinJurorStake::get(); draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(market_id, draws); - >::insert(ALICE, JurorInfo { stake, active_lock: slashable }); + >::insert( + ALICE, + JurorInfo { stake, active_lock: slashable, prepare_exit_at: None }, + ); } fn set_alice_after_vote( @@ -183,6 +186,7 @@ fn exit_court_successfully_removes_a_juror_and_frees_balances() { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + run_blocks(InflationPeriod::get()); assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); assert_eq!(Jurors::::iter().count(), 0); assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE); @@ -199,7 +203,7 @@ fn join_court_successfully_stores_required_data() { System::assert_last_event(Event::JurorJoined { juror: ALICE, stake: amount }.into()); assert_eq!( Jurors::::iter().next().unwrap(), - (ALICE, JurorInfo { stake: amount, active_lock: 0u128 }) + (ALICE, JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None }) ); assert_eq!(Balances::free_balance(ALICE), alice_free_balance_before); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); @@ -223,7 +227,7 @@ fn join_court_works_multiple_joins() { ); assert_eq!( Jurors::::iter().collect::)>>(), - vec![(ALICE, JurorInfo { stake: amount, active_lock: 0u128 })] + vec![(ALICE, JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None })] ); assert_ok!(Court::join_court(Origin::signed(BOB), amount)); @@ -238,11 +242,11 @@ fn join_court_works_multiple_joins() { assert_eq!(Jurors::::iter().count(), 2); assert_eq!( Jurors::::get(ALICE).unwrap(), - JurorInfo { stake: amount, active_lock: 0u128 } + JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None } ); assert_eq!( Jurors::::get(BOB).unwrap(), - JurorInfo { stake: amount, active_lock: 0u128 } + JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None } ); let higher_amount = amount + 1; @@ -259,11 +263,11 @@ fn join_court_works_multiple_joins() { assert_eq!(Jurors::::iter().count(), 2); assert_eq!( Jurors::::get(BOB).unwrap(), - JurorInfo { stake: amount, active_lock: 0u128 } + JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None } ); assert_eq!( Jurors::::get(ALICE).unwrap(), - JurorInfo { stake: higher_amount, active_lock: 0u128 } + JurorInfo { stake: higher_amount, active_lock: 0u128, prepare_exit_at: None } ); }); } @@ -276,7 +280,10 @@ fn join_court_saves_consumed_stake_and_active_lock_for_double_join() { let consumed_stake = min; let active_lock = min + 1; - Jurors::::insert(ALICE, JurorInfo { stake: amount, active_lock }); + Jurors::::insert( + ALICE, + JurorInfo { stake: amount, active_lock, prepare_exit_at: None }, + ); let juror_pool = vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake }]; JurorPool::::put::>(juror_pool.try_into().unwrap()); @@ -475,6 +482,8 @@ fn exit_court_works_without_active_lock() { assert!(JurorPool::::get().into_inner().is_empty()); assert!(Jurors::::get(ALICE).is_some()); + run_blocks(InflationPeriod::get()); + assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); System::assert_last_event( @@ -495,16 +504,22 @@ fn exit_court_works_with_active_lock() { assert_eq!( >::get(ALICE).unwrap(), - JurorInfo { stake: amount, active_lock: 0 } + JurorInfo { stake: amount, active_lock: 0, prepare_exit_at: None } ); // assume that `choose_multiple_weighted` has set the active_lock - >::insert(ALICE, JurorInfo { stake: amount, active_lock }); + >::insert( + ALICE, + JurorInfo { stake: amount, active_lock, prepare_exit_at: None }, + ); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); + let now = >::block_number(); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); assert!(JurorPool::::get().into_inner().is_empty()); + run_blocks(InflationPeriod::get()); + assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); System::assert_last_event( Event::JurorExited { juror: ALICE, exit_amount: amount - active_lock, active_lock } @@ -512,7 +527,7 @@ fn exit_court_works_with_active_lock() { ); assert_eq!( Jurors::::get(ALICE).unwrap(), - JurorInfo { stake: active_lock, active_lock } + JurorInfo { stake: active_lock, active_lock, prepare_exit_at: Some(now) } ); assert_eq!(Balances::locks(ALICE), vec![the_lock(active_lock)]); }); @@ -534,6 +549,8 @@ fn exit_court_fails_juror_not_prepared_to_exit() { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + run_blocks(InflationPeriod::get()); + assert_noop!( Court::exit_court(Origin::signed(ALICE), ALICE), Error::::JurorNotPreparedToExit @@ -541,6 +558,23 @@ fn exit_court_fails_juror_not_prepared_to_exit() { }); } +#[test] +fn exit_court_fails_if_inflation_period_not_over() { + ExtBuilder::default().build().execute_with(|| { + let amount = 2 * BASE; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + + assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); + + run_blocks(InflationPeriod::get() - 1); + + assert_noop!( + Court::exit_court(Origin::signed(ALICE), ALICE), + Error::::WaitFullInflationPeriod + ); + }); +} + #[test] fn vote_works() { ExtBuilder::default().build().execute_with(|| { @@ -557,7 +591,10 @@ fn vote_works() { let alice_index = 3usize; draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(market_id, draws); - >::insert(ALICE, JurorInfo { stake: amount, active_lock: slashable }); + >::insert( + ALICE, + JurorInfo { stake: amount, active_lock: slashable, prepare_exit_at: None }, + ); let old_draws = >::get(market_id); @@ -732,7 +769,10 @@ fn reveal_vote_works() { let alice_index = 3usize; draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(market_id, draws); - >::insert(ALICE, JurorInfo { stake: amount, active_lock: slashable }); + >::insert( + ALICE, + JurorInfo { stake: amount, active_lock: slashable, prepare_exit_at: None }, + ); run_to_block(>::get() + 1); @@ -2450,7 +2490,7 @@ fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { for pool_item in DEFAULT_SET_OF_JURORS.iter() { >::insert( pool_item.juror, - JurorInfo { stake: pool_item.stake, active_lock: 0u128 }, + JurorInfo { stake: pool_item.stake, active_lock: 0u128, prepare_exit_at: None }, ); jurors.try_push(pool_item.clone()).unwrap(); } @@ -2506,7 +2546,7 @@ fn random_jurors_returns_a_subset_of_jurors() { for pool_item in DEFAULT_SET_OF_JURORS.iter() { >::insert( pool_item.juror, - JurorInfo { stake: pool_item.stake, active_lock: 0u128 }, + JurorInfo { stake: pool_item.stake, active_lock: 0u128, prepare_exit_at: None }, ); jurors.try_push(pool_item.clone()).unwrap(); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 978ebe53c..94977aaf5 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -28,12 +28,14 @@ use zeitgeist_primitives::types::OutcomeReport; PartialEq, Eq, )] -pub struct JurorInfo { +pub struct JurorInfo { /// The juror's amount in the stake weighted pool. /// This amount is used to find a juror with a binary search on the pool. pub stake: Balance, /// The current amount of funds which are locked in courts. pub active_lock: Balance, + /// The block number when a juror exit from court was requested. + pub prepare_exit_at: Option, } pub struct RawCommitment { From 351f0500b665ea5196ec16bd1f93edbb07a11cc8 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 12 Apr 2023 10:30:11 +0200 Subject: [PATCH 236/368] improve benchmarking and testing --- runtime/common/src/lib.rs | 1 + zrml/court/src/benchmarks.rs | 16 +++++++++++++ zrml/court/src/lib.rs | 24 +++++++++++-------- zrml/court/src/mock.rs | 4 ++-- zrml/court/src/tests.rs | 37 +++++++++++++++++++++++++++++ zrml/court/src/types.rs | 18 +++++++++++--- zrml/court/src/weights.rs | 17 +++++++++++++ zrml/prediction-markets/src/mock.rs | 23 ++++++++++-------- 8 files changed, 115 insertions(+), 25 deletions(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 75c6d9e36..64073e9ae 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -962,6 +962,7 @@ macro_rules! impl_config_traits { type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; + type MonetaryGovernanceOrigin = EnsureRoot; type Random = RandomnessCollectiveFlip; type RequestInterval = RequestInterval; type Slash = Treasury; diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index f29f11f6b..b9af8e408 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -32,6 +32,7 @@ use alloc::vec; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; +use sp_arithmetic::Perbill; use sp_runtime::{ traits::{Bounded, Hash, Saturating, StaticLookup, Zero}, SaturatedConversion, @@ -97,6 +98,7 @@ where for i in 0..number { let juror: T::AccountId = account("juror", i, 0); let stake = max_amount - BalanceOf::::from(i); + let _ = T::Currency::deposit_creating(&juror, stake); >::insert( juror.clone(), JurorInfo { stake, active_lock: >::zero(), prepare_exit_at: None }, @@ -444,6 +446,20 @@ benchmarks! { >::insert(market_id, draws); }: _(RawOrigin::Signed(caller), market_id) + set_inflation { + let inflation = Perbill::from_percent(10); + }: _(RawOrigin::Root, inflation) + + handle_inflation { + let j in 1..T::MaxJurors::get(); + fill_pool::(j)?; + + >::set_block_number(T::InflationPeriod::get()); + let now = >::block_number(); + }: { + Court::::handle_inflation(now); + } + impl_benchmark_test_suite!( Court, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 1c0c1110d..89b11a0ad 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -245,13 +245,14 @@ mod pallet { pub type RequestBlock = StorageValue<_, T::BlockNumber, ValueQuery>; #[pallet::type_value] - pub fn DefaultInflation() -> Perbill { + pub fn DefaultYearlyInflation() -> Perbill { Perbill::from_perthousand(20u32) } /// The current inflation rate. #[pallet::storage] - pub type YearlyInflation = StorageValue<_, Perbill, ValueQuery, DefaultInflation>; + pub type YearlyInflation = + StorageValue<_, Perbill, ValueQuery, DefaultYearlyInflation>; #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] @@ -396,10 +397,11 @@ mod pallet { fn on_initialize(now: T::BlockNumber) -> Weight { let mut total_weight: Weight = Weight::zero(); total_weight = total_weight.saturating_add(Self::handle_inflation(now)); + total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); if now >= >::get() { let future_request = now.saturating_add(T::RequestInterval::get()); >::put(future_request); - total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); } total_weight } @@ -462,13 +464,13 @@ mod pallet { .all(|(a, b)| lowest_stake <= a.stake && a == b) }); ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); - lowest_item.map(|pool_item| { + if let Some(pool_item) = lowest_item { if let Some(mut lowest_juror_info) = >::get(&pool_item.juror) { let now = >::block_number(); lowest_juror_info.prepare_exit_at = Some(now); >::insert(&pool_item.juror, lowest_juror_info); } - }); + } // remove the lowest staked juror jurors.remove(0); } @@ -858,7 +860,7 @@ mod pallet { let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { - pre_vote: request_block, + pre_vote_end: request_block, vote_period: T::CourtVotePeriod::get(), aggregation_period: T::CourtAggregationPeriod::get(), appeal_period: T::CourtAppealPeriod::get(), @@ -970,8 +972,7 @@ mod pallet { Ok(Some(T::WeightInfo::reassign_juror_stakes(draws_len)).into()) } - // TODO - #[pallet::weight(5000)] + #[pallet::weight(T::WeightInfo::set_inflation())] #[transactional] pub fn set_inflation(origin: OriginFor, inflation: Perbill) -> DispatchResult { T::MonetaryGovernanceOrigin::ensure_origin(origin)?; @@ -1002,6 +1003,7 @@ mod pallet { ); let jurors = >::get(); + let jurors_len = jurors.len() as u32; let total_stake = jurors.iter().fold(0u128, |acc, pool_item| { acc.saturating_add(pool_item.stake.saturated_into::()) }); @@ -1019,8 +1021,10 @@ mod pallet { }); } } + + return T::WeightInfo::handle_inflation(jurors_len); } - // TODO: add weight + Weight::zero() } @@ -1513,7 +1517,7 @@ mod pallet { let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { - pre_vote: request_block, + pre_vote_end: request_block, vote_period: T::CourtVotePeriod::get(), aggregation_period: T::CourtAggregationPeriod::get(), appeal_period: T::CourtAppealPeriod::get(), diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index f36f1b267..6ef2fc32c 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -26,7 +26,7 @@ use frame_support::{ traits::{Everything, Hooks, NeverEnsureOrigin}, PalletId, }; -use frame_system::EnsureSignedBy; +use frame_system::{EnsureRoot, EnsureSignedBy}; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, @@ -153,7 +153,7 @@ impl crate::Config for Runtime { type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; - type MonetaryGovernanceOrigin = EnsureSignedBy; + type MonetaryGovernanceOrigin = EnsureRoot; type CourtPalletId = CourtPalletId; type Random = MockStorage; type RequestInterval = RequestInterval; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 10113e0b8..141aea905 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -2557,3 +2557,40 @@ fn random_jurors_returns_a_subset_of_jurors() { } }); } + +#[test] +fn handle_inflation_works() { + ExtBuilder::default().build().execute_with(|| { + let mut jurors = >::get(); + let mut free_balances_before = BTreeMap::new(); + let jurors_list = vec![1000, 10_000, 100_000, 1_000_000, 10_000_000]; + for number in jurors_list.iter() { + let stake = *number; + let juror = *number; + let _ = Balances::deposit(&juror, stake).unwrap(); + free_balances_before.insert(juror, stake); + jurors.try_push(JurorPoolItem { stake, juror, consumed_stake: 0 }).unwrap(); + } + >::put(jurors.clone()); + + let inflation_period = InflationPeriod::get(); + run_to_block(inflation_period); + let now = >::block_number(); + Court::handle_inflation(now); + + let free_balance_after_0 = Balances::free_balance(jurors_list[0]); + assert_eq!(free_balance_after_0 - free_balances_before[&jurors_list[0]], 43_286_841); + + let free_balance_after_1 = Balances::free_balance(jurors_list[1]); + assert_eq!(free_balance_after_1 - free_balances_before[&jurors_list[1]], 432_868_409); + + let free_balance_after_2 = Balances::free_balance(jurors_list[2]); + assert_eq!(free_balance_after_2 - free_balances_before[&jurors_list[2]], 4_328_684_088); + + let free_balance_after_3 = Balances::free_balance(jurors_list[3]); + assert_eq!(free_balance_after_3 - free_balances_before[&jurors_list[3]], 43_286_840_884); + + let free_balance_after_4 = Balances::free_balance(jurors_list[4]); + assert_eq!(free_balance_after_4 - free_balances_before[&jurors_list[4]], 432_868_408_838); + }); +} diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 94977aaf5..0aa1cd443 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -38,14 +38,21 @@ pub struct JurorInfo { pub prepare_exit_at: Option, } +/// The raw information behind the secret hash of a juror's vote. pub struct RawCommitment { + /// The juror's account id. pub juror: AccountId, + /// The outcome which the juror voted for. pub outcome: OutcomeReport, + /// The salt which was used to hash the vote. pub salt: Hash, } +/// The raw information which is hashed to create the secret hash of a juror's vote. pub struct CommitmentMatcher { + /// The juror's hashed commitment pub hashed: Hash, + /// The raw commitment which is intended to lead to the hashed commitment. pub raw: RawCommitment, } @@ -153,10 +160,15 @@ pub struct CourtInfo { pub cycle_ends: CycleEnds, } +/// The timing information about a court case. pub struct RoundTiming { - pub pre_vote: BlockNumber, + /// The end block of the pre-vote period. + pub pre_vote_end: BlockNumber, + /// The block duration for votes. pub vote_period: BlockNumber, + /// The block duration for revealing votes. pub aggregation_period: BlockNumber, + /// The block duration for appeals. pub appeal_period: BlockNumber, } @@ -164,7 +176,7 @@ impl CourtInfo { pub fn new(round_timing: RoundTiming) -> Self { - let pre_vote = round_timing.pre_vote; + let pre_vote = round_timing.pre_vote_end; let vote = pre_vote.saturating_add(round_timing.vote_period); let aggregation = vote.saturating_add(round_timing.aggregation_period); let appeal = aggregation.saturating_add(round_timing.appeal_period); @@ -174,7 +186,7 @@ impl } pub fn update_lifecycle(&mut self, round_timing: RoundTiming) { - self.cycle_ends.pre_vote = round_timing.pre_vote; + self.cycle_ends.pre_vote = round_timing.pre_vote_end; self.cycle_ends.vote = self.cycle_ends.pre_vote.saturating_add(round_timing.vote_period); self.cycle_ends.aggregation = self.cycle_ends.vote.saturating_add(round_timing.aggregation_period); diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 215549b41..5ea2ca7f2 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -54,6 +54,8 @@ pub trait WeightInfoZeitgeist { fn reveal_vote(d: u32) -> Weight; fn appeal(d: u32, a: u32, r: u32) -> Weight; fn reassign_juror_stakes(d: u32) -> Weight; + fn set_inflation() -> Weight; + fn handle_inflation(j: u32) -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -162,4 +164,19 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) } + // Storage: Court YearlyInflation (r:0 w:1) + fn set_inflation() -> Weight { + Weight::from_ref_time(6_000_000).saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: Court YearlyInflation (r:1 w:0) + // Storage: Court JurorPool (r:1 w:0) + // Storage: System Account (r:1 w:1) + fn handle_inflation(j: u32) -> Weight { + Weight::from_ref_time(0) + // Standard Error: 4_000 + .saturating_add(Weight::from_ref_time(13_075_000).saturating_mul(j.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) + } } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index af13540d8..6e73f9d28 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -27,7 +27,7 @@ use frame_support::{ construct_runtime, ord_parameter_types, parameter_types, traits::{Everything, NeverEnsureOrigin, OnFinalize, OnInitialize}, }; -use frame_system::EnsureSignedBy; +use frame_system::{EnsureRoot, EnsureSignedBy}; #[cfg(feature = "parachain")] use orml_asset_registry::AssetMetadata; use sp_arithmetic::per_things::Percent; @@ -38,17 +38,17 @@ use sp_runtime::{ use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; use zeitgeist_primitives::{ constants::mock::{ - AppealBond, AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, + AppealBond, AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, BlocksPerYear, CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, - LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, - MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, - MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, - MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, - MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, - MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, - OutsiderBond, PmPalletId, RequestInterval, SimpleDisputesPalletId, SwapsPalletId, - TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, + InflationPeriod, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, + MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, MaxGracePeriod, + MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, + MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, + MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, + MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, + OutcomeFactor, OutsiderBond, PmPalletId, RequestInterval, SimpleDisputesPalletId, + SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -249,6 +249,7 @@ impl zrml_authorized::Config for Runtime { impl zrml_court::Config for Runtime { type AppealBond = AppealBond; + type BlocksPerYear = BlocksPerYear; type DisputeResolution = prediction_markets::Pallet; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; @@ -256,11 +257,13 @@ impl zrml_court::Config for Runtime { type CourtLockId = CourtLockId; type Currency = Balances; type Event = Event; + type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; + type MonetaryGovernanceOrigin = EnsureRoot; type CourtPalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RequestInterval = RequestInterval; From 67aa44dc73d4f792c87709202463efdb1b53ef9b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 12 Apr 2023 13:23:54 +0200 Subject: [PATCH 237/368] add integrity_test --- zrml/court/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 89b11a0ad..e5da29586 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -405,6 +405,10 @@ mod pallet { } total_weight } + + fn integrity_test() { + assert!(!T::BlocksPerYear::get().is_zero(), "Blocks per year assumption changed."); + } } #[pallet::call] @@ -996,6 +1000,7 @@ mod pallet { let blocks_per_year = T::BlocksPerYear::get() .saturated_into::() .saturated_into::>(); + debug_assert!(!T::BlocksPerYear::get().is_zero()); let issue_per_block = yearly_inflation_amount / blocks_per_year.max(One::one()); let inflation_period_mint = issue_per_block.saturating_mul( From c3b61cb3b9c8e80177b1bdb9a154f9fe9245fa2a Mon Sep 17 00:00:00 2001 From: Chralt Date: Wed, 12 Apr 2023 13:28:36 +0200 Subject: [PATCH 238/368] Update zrml/simple-disputes/src/mock.rs Co-authored-by: Harald Heckmann --- zrml/simple-disputes/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index 7fa175ab4..bc46fdfe3 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -61,8 +61,8 @@ construct_runtime!( NodeBlock = BlockTest, UncheckedExtrinsic = UncheckedExtrinsicTest, { - Balances: pallet_balances::{Call, Config, Event, Pallet, Storage}, AssetManager: orml_currencies::{Call, Pallet, Storage}, + Balances: pallet_balances::{Call, Config, Event, Pallet, Storage}, MarketCommons: zrml_market_commons::{Pallet, Storage}, SimpleDisputes: zrml_simple_disputes::{Event, Pallet, Storage}, System: frame_system::{Call, Config, Event, Pallet, Storage}, From 11960ded8b198ceea71434198e0320eece9e4908 Mon Sep 17 00:00:00 2001 From: Chralt Date: Wed, 12 Apr 2023 13:28:47 +0200 Subject: [PATCH 239/368] Update zrml/simple-disputes/src/mock.rs Co-authored-by: Harald Heckmann --- zrml/simple-disputes/src/mock.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zrml/simple-disputes/src/mock.rs b/zrml/simple-disputes/src/mock.rs index bc46fdfe3..5119f1343 100644 --- a/zrml/simple-disputes/src/mock.rs +++ b/zrml/simple-disputes/src/mock.rs @@ -113,11 +113,11 @@ impl DisputeResolutionApi for NoopResolution { impl crate::Config for Runtime { type AssetManager = AssetManager; type Event = (); - type OutcomeBond = OutcomeBond; - type OutcomeFactor = OutcomeFactor; type DisputeResolution = NoopResolution; type MarketCommons = MarketCommons; type MaxDisputes = MaxDisputes; + type OutcomeBond = OutcomeBond; + type OutcomeFactor = OutcomeFactor; type PalletId = SimpleDisputesPalletId; type WeightInfo = zrml_simple_disputes::weights::WeightInfo; } From 6fc919763245d047f8eb60ff852c4088ca7dc551 Mon Sep 17 00:00:00 2001 From: Chralt Date: Wed, 12 Apr 2023 13:40:06 +0200 Subject: [PATCH 240/368] Update zrml/prediction-markets/src/lib.rs Co-authored-by: Harald Heckmann --- zrml/prediction-markets/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 940565e93..15e56c316 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1809,7 +1809,7 @@ mod pallet { MarketInsufficientSubsidy(MarketIdOf, MarketStatus), /// A market has been closed \[market_id\] MarketClosed(MarketIdOf), - /// A market has been disputed \[market_id, new_market_status, new_outcome\] + /// A market has been disputed \[market_id, new_market_status\] MarketDisputed(MarketIdOf, MarketStatus), /// An advised market has ended before it was approved or rejected. \[market_id\] MarketExpired(MarketIdOf), From c6386796b529d77719c198107f2ca5265ea85af3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 12 Apr 2023 13:42:24 +0200 Subject: [PATCH 241/368] add doc string --- zrml/prediction-markets/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 15e56c316..6b0ba23cc 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2221,6 +2221,7 @@ mod pallet { Ok((ids_len, mdm_len)) } + /// The dispute mechanism is intended to clear its own storage here. fn clear_dispute_mechanism(market_id: &MarketIdOf) -> DispatchResult { let market = >::market(market_id)?; match market.dispute_mechanism { From 553e082af83ef27fd8cc008776159351f0228b02 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 12 Apr 2023 13:48:49 +0200 Subject: [PATCH 242/368] add doc strings --- zrml/prediction-markets/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 6b0ba23cc..de5ce1606 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2486,6 +2486,8 @@ mod pallet { } } + /// Handle a market resolution, which is currently in the reported state. + /// Returns the resolved outcome of a market, which is the reported outcome. fn resolve_reported_market( market_id: &MarketIdOf, market: &MarketOf, @@ -2506,6 +2508,8 @@ mod pallet { Ok(report.outcome.clone()) } + /// Handle a market resolution, which is currently in the disputed state. + /// Returns the resolved outcome of a market. fn resolve_disputed_market( market_id: &MarketIdOf, market: &MarketOf, @@ -2537,6 +2541,7 @@ mod pallet { Ok(resolved_outcome) } + /// Get the outcome the market should resolve to. fn get_resolved_outcome( market_id: &MarketIdOf, market: &MarketOf, @@ -2568,6 +2573,7 @@ mod pallet { Ok(resolved_outcome_option.unwrap_or_else(|| reported_outcome.clone())) } + /// Manage the outstanding bonds (oracle, outsider, dispute) of the market. fn settle_bonds( market_id: &MarketIdOf, market: &MarketOf, From 55fdee0774285b5fc3358a74bf0f4108496205c1 Mon Sep 17 00:00:00 2001 From: Harald Heckmann Date: Mon, 3 Apr 2023 12:40:12 +0200 Subject: [PATCH 243/368] Reduce settle_bonds LOC --- zrml/prediction-markets/src/lib.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index de5ce1606..7c3a11338 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2621,7 +2621,7 @@ mod pallet { } } - let mut correct_disputor = None; + if let Some(bond) = &market.bonds.dispute { if !bond.is_settled { if is_correct { @@ -2630,19 +2630,13 @@ mod pallet { } else { // If the report outcome was wrong, the dispute was justified Self::unreserve_dispute_bond(market_id)?; - correct_disputor = Some(bond.who.clone()); + CurrencyOf::::resolve_creating(&bond.who, overall_imbalance); + overall_imbalance = NegativeImbalanceOf::::zero(); } } } - let mut imbalance_left = >::zero(); - if let Some(disputor) = correct_disputor { - CurrencyOf::::resolve_creating(&disputor, overall_imbalance); - } else { - imbalance_left = overall_imbalance; - } - - Ok(imbalance_left) + Ok(overall_imbalance) } pub fn on_resolution( From 0efc405603c6cbf32b5ab022bfcce4527c3555fa Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 12 Apr 2023 14:00:15 +0200 Subject: [PATCH 244/368] cargo fmt --- zrml/prediction-markets/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 7c3a11338..4de5c08a9 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2621,7 +2621,6 @@ mod pallet { } } - if let Some(bond) = &market.bonds.dispute { if !bond.is_settled { if is_correct { From aa09b01e1c1536cc7a23099a0c0a0d4a8b2afd7a Mon Sep 17 00:00:00 2001 From: Chralt Date: Wed, 12 Apr 2023 14:02:47 +0200 Subject: [PATCH 245/368] Update zrml/prediction-markets/src/migrations.rs Co-authored-by: Harald Heckmann --- zrml/prediction-markets/src/migrations.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index 45bfca43e..aa2bb6cd9 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -457,9 +457,7 @@ where // important drain disputes storage item from prediction markets pallet for (market_id, old_disputes) in crate::Disputes::::drain() { - total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); - - total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); + total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(1,1)); if let Ok(market) = >::market(&market_id) { match market.dispute_mechanism { MarketDisputeMechanism::Authorized => continue, From 2c15b969a2a9628f18dd871523b6f13fcef34436 Mon Sep 17 00:00:00 2001 From: Chralt Date: Wed, 12 Apr 2023 14:03:59 +0200 Subject: [PATCH 246/368] Update zrml/prediction-markets/src/migrations.rs Co-authored-by: Harald Heckmann --- zrml/prediction-markets/src/migrations.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index aa2bb6cd9..d52fd3cb8 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -491,10 +491,8 @@ where } // switch to new reserve identifier for simple disputes - let sd_pallet_id = zeitgeist_primitives::constants::SD_PALLET_ID; - let sd_reserve_id = sd_pallet_id.0; - let pm_pallet_id = zeitgeist_primitives::constants::PM_PALLET_ID; - let pm_reserve_id = pm_pallet_id.0; + let sd_reserve_id = >::reserve_id(); + let pm_reserve_id = >::reserve_id(); // charge weight defensivly for unreserve_named // https://github.com/open-web3-stack/open-runtime-module-library/blob/24f0a8b6e04e1078f70d0437fb816337cdf4f64c/tokens/src/lib.rs#L1516-L1547 From a71bb5c318fe0e2f37a8421b995c3a9fff5a32f9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 12 Apr 2023 15:17:03 +0200 Subject: [PATCH 247/368] apply review suggestion --- primitives/src/market.rs | 11 +++++++++++ primitives/src/traits/dispute_api.rs | 4 ++-- zrml/authorized/src/lib.rs | 5 +++-- zrml/court/src/lib.rs | 5 +++-- zrml/prediction-markets/src/lib.rs | 17 +++++++++++------ zrml/prediction-markets/src/migrations.rs | 2 +- zrml/simple-disputes/src/lib.rs | 20 +++++++++++--------- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/primitives/src/market.rs b/primitives/src/market.rs index 0c2c4386a..6eeb4e9ca 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -177,6 +177,17 @@ pub enum MarketCreation { Advised, } +/// Defines a global dispute item for the initialisation of a global dispute. +pub struct GlobalDisputeItem { + /// The account that already paid somehow for the outcome. + pub owner: AccountId, + /// The outcome that was already paid for + /// and should be added as vote outcome inside global disputes. + pub outcome: OutcomeReport, + /// The initial amount added in the global dispute vote system initially for the outcome. + pub initial_vote_amount: Balance, +} + // TODO to remove, when Disputes storage item is removed #[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub struct OldMarketDispute { diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index bcb6734c1..2a137f243 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -20,7 +20,7 @@ extern crate alloc; use crate::{ outcome_report::OutcomeReport, - types::{Asset, Market}, + types::{Asset, GlobalDisputeItem, Market}, }; use alloc::vec::Vec; use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight}; @@ -38,7 +38,7 @@ type MarketOfDisputeApi = Market< >; type GlobalDisputeItemOfDisputeApi = - (OutcomeReport, ::AccountId, ::Balance); + GlobalDisputeItem<::AccountId, ::Balance>; pub trait DisputeApi { type AccountId; diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 3b6f231b5..142a5cb1b 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -49,7 +49,8 @@ mod pallet { use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, types::{ - Asset, AuthorityReport, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport, + Asset, AuthorityReport, GlobalDisputeItem, Market, MarketDisputeMechanism, + MarketStatus, OutcomeReport, }, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -264,7 +265,7 @@ mod pallet { fn on_global_dispute( _: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 86541a834..e16c4f1fe 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -68,7 +68,8 @@ mod pallet { use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, types::{ - Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport, + Asset, GlobalDisputeItem, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, + OutcomeReport, }, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -628,7 +629,7 @@ mod pallet { fn on_global_dispute( _: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 4de5c08a9..4948976fe 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -64,9 +64,9 @@ mod pallet { constants::MILLISECS_PER_BLOCK, traits::{DisputeApi, DisputeResolutionApi, Swaps, ZeitgeistAssetManager}, types::{ - Asset, Bond, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, - MarketPeriod, MarketStatus, MarketType, MultiHash, OldMarketDispute, OutcomeReport, - Report, ScalarPosition, ScoringRule, SubsidyUntil, + Asset, Bond, Deadlines, GlobalDisputeItem, Market, MarketBonds, MarketCreation, + MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, MultiHash, + OldMarketDispute, OutcomeReport, Report, ScalarPosition, ScoringRule, SubsidyUntil, }, }; #[cfg(feature = "with-global-disputes")] @@ -1448,7 +1448,7 @@ mod pallet { }; ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); - let initial_vote_outcomes = match market.dispute_mechanism { + let gd_items = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_global_dispute(&market_id, &market)? } @@ -1468,8 +1468,13 @@ mod pallet { )?; // push vote outcomes other than the report outcome - for (outcome, owner, bond) in initial_vote_outcomes { - T::GlobalDisputes::push_voting_outcome(&market_id, outcome, &owner, bond)?; + for GlobalDisputeItem { outcome, owner, initial_vote_amount } in gd_items { + T::GlobalDisputes::push_voting_outcome( + &market_id, + outcome, + &owner, + initial_vote_amount, + )?; } // TODO(#372): Allow court with global disputes. diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index d52fd3cb8..7e45d23c7 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -457,7 +457,7 @@ where // important drain disputes storage item from prediction markets pallet for (market_id, old_disputes) in crate::Disputes::::drain() { - total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(1,1)); + total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); if let Ok(market) = >::market(&market_id) { match market.dispute_mechanism { MarketDisputeMechanism::Authorized => continue, diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 9028959b6..c649177aa 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -33,7 +33,8 @@ pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi, ZeitgeistAssetManager}, types::{ - Asset, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, OutcomeReport, Report, + Asset, GlobalDisputeItem, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, + OutcomeReport, Report, }, }; @@ -396,19 +397,20 @@ mod pallet { fn on_global_dispute( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - let mut gd_outcomes: Vec<(OutcomeReport, Self::AccountId, Self::Balance)> = Vec::new(); - - for MarketDispute { at: _, by, outcome, bond } in >::get(market_id).iter() { - gd_outcomes.push((outcome.clone(), by.clone(), *bond)); - } - - Ok(gd_outcomes) + Ok(>::get(market_id) + .iter() + .map(|dispute| GlobalDisputeItem { + outcome: dispute.outcome.clone(), + owner: dispute.by.clone(), + initial_vote_amount: dispute.bond, + }) + .collect()) } fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { From 3b789c14236acfd85b9f2e58425d50d5a0146f43 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 13 Apr 2023 04:51:54 +0200 Subject: [PATCH 248/368] add GDItem integration --- primitives/src/market.rs | 1 + zrml/court/src/lib.rs | 12 ++++++------ zrml/court/src/tests.rs | 8 ++++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/primitives/src/market.rs b/primitives/src/market.rs index 6eeb4e9ca..3deb85494 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -178,6 +178,7 @@ pub enum MarketCreation { } /// Defines a global dispute item for the initialisation of a global dispute. +#[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct GlobalDisputeItem { /// The account that already paid somehow for the outcome. pub owner: AccountId, diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9c7ee2e0d..095d08b5a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1663,15 +1663,15 @@ mod pallet { // oracle outcome is added by pm pallet .filter(|a| &a.appealed_outcome != oracle_outcome) .map(|a| { - ( - a.appealed_outcome.clone(), + GlobalDisputeItem { + outcome: a.appealed_outcome.clone(), // we have no better global dispute outcome owner - Self::treasury_account_id(), + owner: Self::treasury_account_id(), // initial vote amount - >::zero(), - ) + initial_vote_amount: >::zero(), + } }) - .collect::>(); + .collect::>>(); let old_draws = Draws::::get(market_id); Self::unlock_jurors_from_last_draw(market_id, old_draws); diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 141aea905..af482ab71 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -49,7 +49,7 @@ use zeitgeist_primitives::{ }, traits::DisputeApi, types::{ - AccountIdTest, Asset, Deadlines, Market, MarketBonds, MarketCreation, + AccountIdTest, Asset, Deadlines, GlobalDisputeItem, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, OutcomeReport, Report, ScoringRule, }, @@ -2163,7 +2163,11 @@ fn on_global_dispute_returns_appealed_outcomes() { let appealed_outcome = OutcomeReport::Scalar(number as u128); let backer = number as u128; let bond = crate::get_appeal_bond::(court.appeals.len()); - gd_outcomes.push((appealed_outcome.clone(), treasury_account, initial_vote_amount)); + gd_outcomes.push(GlobalDisputeItem { + outcome: appealed_outcome.clone(), + owner: treasury_account, + initial_vote_amount, + }); court.appeals.try_push(AppealInfo { backer, bond, appealed_outcome }).unwrap(); } Courts::::insert(market_id, court); From 17063e0a2fb04a25b59c13bc257eb52abe46dbf2 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 13 Apr 2023 13:01:14 +0200 Subject: [PATCH 249/368] add delegations --- zrml/court/src/lib.rs | 310 +++++++++++++++++++++++++++++++++------- zrml/court/src/types.rs | 57 +++++++- 2 files changed, 308 insertions(+), 59 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 095d08b5a..a2d9e99ba 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -39,7 +39,8 @@ pub use types::*; mod pallet { use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CommitmentMatcher, CourtInfo, CourtPalletApi, - CourtStatus, Draw, JurorInfo, JurorPoolItem, RawCommitment, RoundTiming, Vote, + CourtStatus, Draw, JurorInfo, JurorPoolItem, JurorVoteWithStakes, RawCommitment, + RoundTiming, SelectionAdd, SelectionValue, SelfInfo, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -68,7 +69,7 @@ mod pallet { use rand_chacha::ChaCha20Rng; use sp_arithmetic::{per_things::Perquintill, traits::One}; use sp_runtime::{ - traits::{AccountIdConversion, CheckedDiv, Hash, Saturating, StaticLookup, Zero}, + traits::{AccountIdConversion, Hash, Saturating, StaticLookup, Zero}, DispatchError, Perbill, SaturatedConversion, }; use zeitgeist_primitives::{ @@ -143,6 +144,9 @@ mod pallet { #[pallet::constant] type MaxDraws: Get; + #[pallet::constant] + type MaxDelegations: Get; + /// The maximum number of jurors that can be registered. #[pallet::constant] type MaxJurors: Get; @@ -196,23 +200,26 @@ mod pallet { MomentOf, Asset>, >; + pub(crate) type HashOf = ::Hash; pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; - pub(crate) type JurorInfoOf = JurorInfo, BlockNumberFor>; + pub(crate) type DelegatedStakesOf = + BoundedVec<(AccountIdOf, BalanceOf), ::MaxDelegations>; + pub(crate) type SelectionValueOf = SelectionValue, DelegatedStakesOf>; + pub(crate) type DelegationsOf = BoundedVec, ::MaxDelegations>; + pub(crate) type VoteOf = Vote, DelegatedStakesOf>; + pub(crate) type JurorVoteWithStakesOf = JurorVoteWithStakes, BalanceOf>; + pub(crate) type JurorInfoOf = JurorInfo, BlockNumberFor, DelegationsOf>; pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; - pub(crate) type DrawOf = - Draw, BalanceOf, ::Hash>; + pub(crate) type DrawOf = Draw, BalanceOf, HashOf, DelegatedStakesOf>; pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; - pub(crate) type CommitmentMatcherOf = - CommitmentMatcher, ::Hash>; - pub(crate) type RawCommitmentOf = - RawCommitment, ::Hash>; + pub(crate) type CommitmentMatcherOf = CommitmentMatcher, HashOf>; + pub(crate) type RawCommitmentOf = RawCommitment, HashOf>; pub type CacheSize = ConstU32<64>; - #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); @@ -392,6 +399,8 @@ mod pallet { WaitFullInflationPeriod, /// The `prepare_exit_at` field is not present. PrepareExitAtNotPresent, + MaxDelegationsReached, + JurorDelegated, } #[pallet::hooks] @@ -433,12 +442,19 @@ mod pallet { pub fn join_court( origin: OriginFor, amount: BalanceOf, + delegations: Vec, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); let free_balance = T::Currency::free_balance(&who); ensure!(amount <= free_balance, Error::::AmountExceedsBalance); + // TODO what happens if you delegate to your own account? + // TODO Should we allow that you delegate the same account twice? + // TODO No, because it takes resources! + let delegations: DelegationsOf = + delegations.try_into().map_err(|_| Error::::MaxDelegationsReached)?; + let mut jurors = JurorPool::::get(); let (active_lock, consumed_stake) = if let Some(prev_juror_info) = @@ -513,7 +529,10 @@ mod pallet { let jurors_len = jurors.len() as u32; JurorPool::::put(jurors); - let juror_info = JurorInfoOf:: { stake: amount, active_lock, prepare_exit_at: None }; + // TODO: the juror should be still accountable for the last delegations (if drawn) + // TODO: with this you override the last delegations with new delegations + let juror_info = + JurorInfoOf:: { stake: amount, active_lock, prepare_exit_at: None, delegations }; >::insert(&who, juror_info); Self::deposit_event(Event::JurorJoined { juror: who, stake: amount }); @@ -934,7 +953,8 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); }; - let mut valid_winners_and_losers = Vec::with_capacity(draws.len()); + // map delegated jurors to own_slashable, outcome and Vec<(delegator, delegator_stake)> + let mut jurors_to_stakes = BTreeMap::>::new(); for draw in draws { if let Some(mut juror_info) = >::get(&draw.juror) { @@ -957,16 +977,35 @@ mod pallet { slash_juror(&draw.juror, draw.slashable); } Vote::Revealed { commitment: _, outcome, salt: _ } => { - valid_winners_and_losers.push((draw.juror, outcome, draw.slashable)); + jurors_to_stakes + .entry(draw.juror) + .or_insert(JurorVoteWithStakes { self_info: None, delegations: vec![] }) + .self_info = Some(SelfInfo { slashable: draw.slashable, outcome }); + } + Vote::Delegated { delegated_stakes } => { + let delegator = draw.juror; + for (j, delegated_stake) in delegated_stakes { + let delegations = &mut jurors_to_stakes + .entry(j) + .or_insert(JurorVoteWithStakes { + self_info: None, + delegations: vec![], + }) + .delegations; + match delegations.binary_search_by_key(&delegator, |(d, _)| d.clone()) { + Ok(i) => { + delegations[i].1.saturating_add(delegated_stake); + } + Err(i) => { + delegations.insert(i, (delegator.clone(), delegated_stake)); + } + } + } } } } - Self::slash_losers_to_award_winners( - &market_id, - valid_winners_and_losers.as_slice(), - &winner, - ); + Self::slash_losers_to_award_winners(&market_id, jurors_to_stakes, &winner); court.status = CourtStatus::Reassigned; >::insert(market_id, court); @@ -1083,9 +1122,14 @@ mod pallet { // The added active lock amount is noted in the Jurors map. fn update_active_lock( juror: &T::AccountId, - selections: &BTreeMap)>, + selections: &BTreeMap>, ) -> BalanceOf { - if let Some((_, total_lock_added)) = selections.get(juror) { + if let Some(SelectionValue { + weight: _, + slashable: total_lock_added, + delegated_stakes: _, + }) = selections.get(juror) + { if let Some(mut juror_info) = >::get(juror) { juror_info.active_lock = juror_info.active_lock.saturating_add(*total_lock_added); @@ -1109,21 +1153,100 @@ mod pallet { false } + fn add_delegated_juror( + delegated_stakes: &mut DelegatedStakesOf, + delegated_juror: &T::AccountId, + amount: BalanceOf, + ) { + match delegated_stakes.binary_search_by_key(&delegated_juror, |(j, _)| j) { + Ok(index) => { + (*delegated_stakes)[index].1 = delegated_stakes[index].1.saturating_add(amount); + } + Err(index) => { + let _ = (*delegated_stakes) + .try_insert(index, (delegated_juror.clone(), amount)) + .map_err(|_| { + debug_assert!( + false, + "BoundedVec insertion should not fail, because the length of \ + jurors is ensured for delegations." + ); + }); + } + } + } + // Updates the `selections` map for the juror and the lock amount. // If `juror` does not already exist in `selections`, // the vote weight is set to 1 and the lock amount is initially set. // For each call on the same juror, the vote weight is incremented by one // and the lock amount is added to the previous amount. fn update_selections( - selections: &mut BTreeMap)>, + selections: &mut BTreeMap>, juror: &T::AccountId, - lock: BalanceOf, + sel_add: SelectionAdd, BalanceOf>, ) { - if let Some((weight, prev_lock)) = selections.get_mut(juror) { - *weight = weight.saturating_add(1); - *prev_lock = prev_lock.saturating_add(lock); + if let Some(SelectionValue { weight, slashable, delegated_stakes }) = + selections.get_mut(juror) + { + match sel_add { + SelectionAdd::SelfStake { lock } => { + *weight = weight.saturating_add(1); + *slashable = slashable.saturating_add(lock); + } + SelectionAdd::DelegationStake { delegated_juror, lock } => { + *slashable = slashable.saturating_add(lock); + let delegated_stakes_before = delegated_stakes.clone(); + Self::add_delegated_juror(delegated_stakes, &delegated_juror, lock); + debug_assert!( + delegated_stakes_before + .iter() + .find(|(j, _)| j == &delegated_juror) + .map_or(>::zero(), |(_, amount)| *amount) + + lock + == delegated_stakes + .iter() + .find(|(j, _)| j == &delegated_juror) + .map(|(_, amount)| *amount) + .unwrap(), + "Delegated juror stake should contain the updated amount." + ); + } + SelectionAdd::DelegationWeight => { + *weight = weight.saturating_add(1); + } + }; } else { - selections.insert(juror.clone(), (1, lock)); + match sel_add { + SelectionAdd::SelfStake { lock } => { + selections.insert( + juror.clone(), + SelectionValue { + weight: 1, + slashable: lock, + delegated_stakes: Default::default(), + }, + ); + } + SelectionAdd::DelegationStake { delegated_juror, lock } => { + let mut delegated_stakes = DelegatedStakesOf::::default(); + Self::add_delegated_juror(&mut delegated_stakes, &delegated_juror, lock); + selections.insert( + juror.clone(), + SelectionValue { weight: 0, slashable: lock, delegated_stakes }, + ); + } + SelectionAdd::DelegationWeight => { + selections.insert( + juror.clone(), + SelectionValue { + weight: 1, + slashable: >::zero(), + delegated_stakes: Default::default(), + }, + ); + } + }; } } @@ -1133,7 +1256,7 @@ mod pallet { fn process_juror_pool( jurors: &mut JurorPoolOf, random_set: &mut BTreeSet, - selections: &mut BTreeMap)>, + selections: &mut BTreeMap>, ) { let mut current_weight = 0u128; for JurorPoolItem { stake, juror, consumed_stake } in jurors.iter_mut() { @@ -1141,6 +1264,7 @@ mod pallet { let mut unconsumed = stake.saturating_sub(*consumed_stake); let upper_bound = current_weight.saturating_add(unconsumed.saturated_into::()); + let mut selection_count = 0usize; // this always gets the lowest random number first and maybe removes it for random_number in random_set.clone().iter() { @@ -1148,8 +1272,34 @@ mod pallet { let lock_added = unconsumed.min(T::MinJurorStake::get()); unconsumed = unconsumed.saturating_sub(lock_added); - Self::update_selections(selections, juror, lock_added); + let delegations = >::get(juror.clone()) + .map_or(vec![], |juror_info| juror_info.delegations.into_inner()); + if delegations.is_empty() { + let sel_add = SelectionAdd::SelfStake { lock: lock_added }; + Self::update_selections(selections, juror, sel_add); + } else { + let sel_add = SelectionAdd::DelegationWeight; + debug_assert!(!delegations.is_empty()); + let delegation_index = selection_count % delegations.len(); + let delegated_juror = match delegations.get(delegation_index) { + Some(delegated_juror) => delegated_juror, + None => { + log::error!("Delegation with modulo index should exist!"); + debug_assert!(false); + &juror + } + }; + Self::update_selections(selections, &delegated_juror, sel_add); + + let sel_add = SelectionAdd::DelegationStake { + delegated_juror: delegated_juror.clone(), + lock: lock_added, + }; + Self::update_selections(selections, juror, sel_add); + } + random_set.remove(random_number); + selection_count = selection_count.saturating_add(1usize); } else { break; } @@ -1168,14 +1318,22 @@ mod pallet { // Converts the `selections` map into a vector of `Draw` structs. fn convert_selections_to_draws( - selections: BTreeMap)>, + selections: BTreeMap>, ) -> Vec> { selections .into_iter() - .map(|(juror, (weight, slashable))| Draw { + .map(|(juror, SelectionValue { weight, slashable, delegated_stakes })| Draw { juror, weight, - vote: Vote::Drawn, + vote: if !delegated_stakes.is_empty() { + debug_assert!( + weight.is_zero(), + "Jurors who delegated shouldn't have voting weight." + ); + Vote::Delegated { delegated_stakes } + } else { + Vote::Drawn + }, slashable, }) .collect() @@ -1190,7 +1348,7 @@ mod pallet { ) -> Result>, DispatchError> { let total_weight = Self::get_unconsumed_stake(jurors); let mut random_set = Self::get_n_random_numbers(number, total_weight)?; - let mut selections = BTreeMap::)>::new(); + let mut selections = BTreeMap::>::new(); Self::process_juror_pool(jurors, &mut random_set, &mut selections); @@ -1342,23 +1500,60 @@ mod pallet { // Slash the losers and use the slashed amount plus the reward pot to reward the winners. fn slash_losers_to_award_winners( market_id: &MarketIdOf, - valid_winners_and_losers: &[(T::AccountId, OutcomeReport, BalanceOf)], + jurors_to_stakes: BTreeMap>, winner_outcome: &OutcomeReport, ) { let mut total_incentives = >::zero(); - let mut winners = Vec::with_capacity(valid_winners_and_losers.len()); - for (juror, outcome, slashable) in valid_winners_and_losers { - if outcome == winner_outcome { - winners.push(juror); - } else { - let (imb, missing) = T::Currency::slash(juror, *slashable); - total_incentives.subsume(imb); - debug_assert!( - missing.is_zero(), - "Could not slash all of the amount for juror {:?}.", - juror - ); + let slash_all_delegators = + |delegations: &Vec<(T::AccountId, BalanceOf)>| -> NegativeImbalanceOf { + let mut total_imb = >::zero(); + for (delegator, d_slashable) in delegations.iter() { + let (imb, missing) = T::Currency::slash(delegator, *d_slashable); + total_imb.subsume(imb); + debug_assert!( + missing.is_zero(), + "Could not slash all of the amount for delegator {:?}.", + delegator + ); + } + total_imb + }; + + let mut total_winner_stake = BalanceOf::::zero(); + let mut winners = Vec::<(T::AccountId, BalanceOf)>::new(); + for (juror, JurorVoteWithStakes { self_info, delegations }) in jurors_to_stakes.iter() { + match self_info { + Some(SelfInfo { slashable, outcome }) => { + if outcome == winner_outcome { + winners.push((juror.clone(), *slashable)); + total_winner_stake = total_winner_stake.saturating_add(*slashable); + + winners.extend(delegations.clone()); + let total_delegation_stake = delegations + .iter() + .fold(BalanceOf::::zero(), |acc, (_, delegator_stake)| { + acc.saturating_add(*delegator_stake) + }); + total_winner_stake = + total_winner_stake.saturating_add(total_delegation_stake); + } else { + let (imb, missing) = T::Currency::slash(juror, *slashable); + total_incentives.subsume(imb); + debug_assert!( + missing.is_zero(), + "Could not slash all of the amount for juror {:?}.", + juror + ); + + let imb = slash_all_delegators(&delegations); + total_incentives.subsume(imb); + } + } + None => { + let imb = slash_all_delegators(&delegations); + total_incentives.subsume(imb); + } } } @@ -1369,15 +1564,19 @@ mod pallet { debug_assert!(missing.is_zero(), "Could not slash all of the amount for reward pot."); total_incentives.subsume(imb); - if let Some(reward_per_each) = - total_incentives.peek().checked_div(&winners.len().saturated_into()) - { - for juror in winners { - let (actual_reward, leftover) = total_incentives.split(reward_per_each); - total_incentives = leftover; - T::Currency::resolve_creating(juror, actual_reward); - } - } else { + let total_reward = total_incentives.peek(); + for (winner, risked_amount) in winners { + let r = risked_amount.saturated_into::(); + let t = total_winner_stake.saturated_into::(); + let share = Perquintill::from_rational(r, t); + let reward_per_each = (share * total_reward.saturated_into::()) + .saturated_into::>(); + let (actual_reward, leftover) = total_incentives.split(reward_per_each); + total_incentives = leftover; + T::Currency::resolve_creating(&winner, actual_reward); + } + + if !total_incentives.peek().is_zero() { // if there are no winners reward the treasury T::Slash::on_unbalanced(total_incentives); } @@ -1476,7 +1675,7 @@ mod pallet { // and check if it matches with the secret hash of the vote. // Otherwise return an error. pub(crate) fn get_hashed_commitment( - vote: Vote, + vote: VoteOf, raw_commitment: RawCommitmentOf, ) -> Result { match vote { @@ -1487,6 +1686,7 @@ mod pallet { Ok(commitment) } Vote::Drawn => Err(Error::::JurorNotVoted.into()), + Vote::Delegated { delegated_stakes: _ } => Err(Error::::JurorDelegated.into()), Vote::Revealed { commitment: _, outcome: _, salt: _ } => { Err(Error::::VoteAlreadyRevealed.into()) } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 0aa1cd443..1149a19fe 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -28,7 +28,7 @@ use zeitgeist_primitives::types::OutcomeReport; PartialEq, Eq, )] -pub struct JurorInfo { +pub struct JurorInfo { /// The juror's amount in the stake weighted pool. /// This amount is used to find a juror with a binary search on the pool. pub stake: Balance, @@ -36,6 +36,7 @@ pub struct JurorInfo { pub active_lock: Balance, /// The block number when a juror exit from court was requested. pub prepare_exit_at: Option, + pub delegations: Delegations, } /// The raw information behind the secret hash of a juror's vote. @@ -67,7 +68,9 @@ pub struct CommitmentMatcher { PartialEq, Eq, )] -pub enum Vote { +pub enum Vote { + /// The juror delegated stake to other jurors. + Delegated { delegated_stakes: DelegatedStakes }, /// The juror was randomly selected to vote in a specific court case. Drawn, /// The juror casted a vote, only providing a hash, which meaning is unknown. @@ -207,14 +210,14 @@ impl PartialEq, Eq, )] -pub struct Draw { +pub struct Draw { /// The juror who was randomly selected. pub juror: AccountId, /// The weight of the juror in this court case. /// The higher the weight the more voice the juror has in the final winner decision. pub weight: u32, /// The information about the vote state. - pub vote: Vote, + pub vote: Vote, /// The amount of funds which can be slashed for this court case. /// This is related to a multiple of `MinStake` to mitigate Sybil attacks. pub slashable: Balance, @@ -240,3 +243,49 @@ pub struct JurorPoolItem { /// of a juror to be selected again. pub consumed_stake: Balance, } + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct SelectionValue { + pub weight: u32, + pub slashable: Balance, + pub delegated_stakes: DelegatedStakes, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub enum SelectionAdd { + SelfStake { lock: Balance }, + DelegationStake { delegated_juror: AccountId, lock: Balance }, + DelegationWeight, +} + +pub struct SelfInfo { + pub slashable: Balance, + pub outcome: OutcomeReport, +} + +pub struct JurorVoteWithStakes { + pub self_info: Option>, + // many delegators can have delegated to the same juror + // that's why the value is a vector and should be sorted (binary search by key) + // the key is the delegator account + // the value is the delegated stake + pub delegations: Vec<(AccountId, Balance)>, +} From ef0ded655d9f659131d21bffce3998bea42b6507 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 13 Apr 2023 15:52:57 +0200 Subject: [PATCH 250/368] improve code structure --- primitives/src/constants/mock.rs | 1 + runtime/battery-station/src/parameters.rs | 2 + runtime/common/src/lib.rs | 1 + runtime/zeitgeist/src/parameters.rs | 2 + zrml/court/src/lib.rs | 403 +++++++++++++--------- zrml/court/src/mock.rs | 3 +- zrml/court/src/tests.rs | 115 +++++- zrml/court/src/types.rs | 4 +- zrml/prediction-markets/src/mock.rs | 5 +- 9 files changed, 359 insertions(+), 177 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index ed198c593..fdd14d3c1 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -44,6 +44,7 @@ parameter_types! { pub const CourtAppealPeriod: BlockNumber = 5; pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const MaxAppeals: u32 = 4; + pub const MaxDelegations: u32 = 5; pub const MaxDraws: u32 = 47; pub const MaxJurors: u32 = 1_000; pub const MinJurorStake: Balance = 50 * CENT; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 58205a37b..f3e615427 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -97,6 +97,8 @@ parameter_types! { pub const InflationPeriod: BlockNumber = 30 * BLOCKS_PER_DAY; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 4; + /// The maximum number of delegations per juror account. + pub const MaxDelegations: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. pub const MaxDraws: u32 = 47; /// The maximum number of jurors that can be registered. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 64073e9ae..3df32e47c 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -959,6 +959,7 @@ macro_rules! impl_config_traits { type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; + type MaxDelegations = MaxDelegations; type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 18fc09b3f..c234e9164 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -97,6 +97,8 @@ parameter_types! { pub const InflationPeriod: BlockNumber = 30 * BLOCKS_PER_DAY; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 4; + /// The maximum number of delegations per juror account. + pub const MaxDelegations: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. pub const MaxDraws: u32 = 47; /// The maximum number of jurors that can be registered. diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index a2d9e99ba..4287669fa 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -22,6 +22,50 @@ extern crate alloc; +use crate::{ + weights::WeightInfoZeitgeist, AppealInfo, CommitmentMatcher, CourtInfo, CourtStatus, Draw, + JurorInfo, JurorPoolItem, JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, + SelectionValue, SelfInfo, Vote, +}; +use alloc::{ + collections::{BTreeMap, BTreeSet}, + vec, + vec::Vec, +}; +use core::marker::PhantomData; +use frame_support::{ + dispatch::DispatchResult, + ensure, log, + pallet_prelude::{ + ConstU32, DispatchResultWithPostInfo, EnsureOrigin, Hooks, OptionQuery, StorageMap, + StorageValue, ValueQuery, Weight, + }, + traits::{ + Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, + NamedReservableCurrency, OnUnbalanced, Randomness, ReservableCurrency, StorageVersion, + WithdrawReasons, + }, + transactional, Blake2_128Concat, BoundedVec, PalletId, +}; +use frame_system::{ + ensure_signed, + pallet_prelude::{BlockNumberFor, OriginFor}, +}; +use rand::{Rng, RngCore, SeedableRng}; +use rand_chacha::ChaCha20Rng; +use sp_arithmetic::{per_things::Perquintill, traits::One}; +use sp_runtime::{ + traits::{AccountIdConversion, Hash, Saturating, StaticLookup, Zero}, + DispatchError, Perbill, SaturatedConversion, +}; +use zeitgeist_primitives::{ + traits::{DisputeApi, DisputeResolutionApi}, + types::{ + Asset, GlobalDisputeItem, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport, + }, +}; +use zrml_market_commons::MarketCommonsPalletApi; + mod benchmarks; mod court_pallet_api; pub mod migrations; @@ -37,48 +81,7 @@ pub use types::*; #[frame_support::pallet] mod pallet { - use crate::{ - weights::WeightInfoZeitgeist, AppealInfo, CommitmentMatcher, CourtInfo, CourtPalletApi, - CourtStatus, Draw, JurorInfo, JurorPoolItem, JurorVoteWithStakes, RawCommitment, - RoundTiming, SelectionAdd, SelectionValue, SelfInfo, Vote, - }; - use alloc::{ - collections::{BTreeMap, BTreeSet}, - vec::Vec, - }; - use core::marker::PhantomData; - use frame_support::{ - dispatch::DispatchResult, - ensure, log, - pallet_prelude::{ - ConstU32, DispatchResultWithPostInfo, EnsureOrigin, Hooks, OptionQuery, StorageMap, - StorageValue, ValueQuery, Weight, - }, - traits::{ - Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, - NamedReservableCurrency, OnUnbalanced, Randomness, ReservableCurrency, StorageVersion, - WithdrawReasons, - }, - transactional, Blake2_128Concat, BoundedVec, PalletId, - }; - use frame_system::{ - ensure_signed, - pallet_prelude::{BlockNumberFor, OriginFor}, - }; - use rand::{Rng, RngCore, SeedableRng}; - use rand_chacha::ChaCha20Rng; - use sp_arithmetic::{per_things::Perquintill, traits::One}; - use sp_runtime::{ - traits::{AccountIdConversion, Hash, Saturating, StaticLookup, Zero}, - DispatchError, Perbill, SaturatedConversion, - }; - use zeitgeist_primitives::{ - traits::{DisputeApi, DisputeResolutionApi}, - types::{ - Asset, GlobalDisputeItem, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport, - }, - }; - use zrml_market_commons::MarketCommonsPalletApi; + use super::*; #[pallet::config] pub trait Config: frame_system::Config { @@ -305,6 +308,12 @@ mod pallet { outcome: OutcomeReport, salt: T::Hash, }, + /// A delegator has delegated their stake to jurors. + DelegatedToJurors { + delegator: T::AccountId, + amount: BalanceOf, + delegated_jurors: Vec, + }, /// A market has been appealed. MarketAppealed { market_id: MarketIdOf, @@ -401,6 +410,8 @@ mod pallet { PrepareExitAtNotPresent, MaxDelegationsReached, JurorDelegated, + SelfDelegationNotAllowed, + IdenticalDelegationsNotAllowed, } #[pallet::hooks] @@ -442,100 +453,43 @@ mod pallet { pub fn join_court( origin: OriginFor, amount: BalanceOf, - delegations: Vec, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); - let free_balance = T::Currency::free_balance(&who); - ensure!(amount <= free_balance, Error::::AmountExceedsBalance); - - // TODO what happens if you delegate to your own account? - // TODO Should we allow that you delegate the same account twice? - // TODO No, because it takes resources! - let delegations: DelegationsOf = - delegations.try_into().map_err(|_| Error::::MaxDelegationsReached)?; - let mut jurors = JurorPool::::get(); + let jurors_len = Self::do_join_court(&who, amount, Default::default())?; - let (active_lock, consumed_stake) = if let Some(prev_juror_info) = - >::get(&who) - { - ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); - let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) - .ok_or(Error::::JurorNeedsToExit)?; - debug_assert!( - prev_juror_info.prepare_exit_at.is_none(), - "If the pool item is found, the prepare_exit_at could have never been written." - ); - let consumed_stake = pool_item.consumed_stake; - jurors.remove(index); - (prev_juror_info.active_lock, consumed_stake) - } else { - if jurors.is_full() { - let lowest_item = jurors.first(); - let lowest_stake = lowest_item - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero); - debug_assert!({ - let mut sorted = jurors.clone(); - sorted.sort_by_key(|pool_item| (pool_item.stake, pool_item.juror.clone())); - jurors.len() == sorted.len() - && jurors - .iter() - .zip(sorted.iter()) - .all(|(a, b)| lowest_stake <= a.stake && a == b) - }); - ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); - if let Some(pool_item) = lowest_item { - if let Some(mut lowest_juror_info) = >::get(&pool_item.juror) { - let now = >::block_number(); - lowest_juror_info.prepare_exit_at = Some(now); - >::insert(&pool_item.juror, lowest_juror_info); - } - } - // remove the lowest staked juror - jurors.remove(0); - } - (>::zero(), >::zero()) - }; + Self::deposit_event(Event::JurorJoined { juror: who, stake: amount }); - match jurors.binary_search_by_key(&(amount, &who), |pool_item| { - (pool_item.stake, &pool_item.juror) - }) { - Ok(_) => { - debug_assert!( - false, - "This should never happen, because we are removing the juror above." - ); - return Err(Error::::JurorTwiceInPool.into()); - } - Err(i) => jurors - .try_insert( - i, - JurorPoolItem { stake: amount, juror: who.clone(), consumed_stake }, - ) - .map_err(|_| { - debug_assert!( - false, - "This should never happen, because we are removing the lowest staked \ - juror above." - ); - Error::::MaxJurorsReached - })?, - }; + Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) + } - T::Currency::set_lock(T::CourtLockId::get(), &who, amount, WithdrawReasons::all()); + #[pallet::weight(T::WeightInfo::join_court(T::MaxJurors::get()))] + #[transactional] + pub fn delegate( + origin: OriginFor, + amount: BalanceOf, + delegations: Vec, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; - let jurors_len = jurors.len() as u32; - JurorPool::::put(jurors); + let mut sorted_delegations: DelegationsOf = + delegations.clone().try_into().map_err(|_| Error::::MaxDelegationsReached)?; + // ensure all elements are different + sorted_delegations.sort(); + let has_duplicates = sorted_delegations + .iter() + .zip(sorted_delegations.iter().skip(1)) + .any(|(x, y)| x == y); + ensure!(!has_duplicates, Error::::IdenticalDelegationsNotAllowed); + ensure!(!sorted_delegations.contains(&who), Error::::SelfDelegationNotAllowed); - // TODO: the juror should be still accountable for the last delegations (if drawn) - // TODO: with this you override the last delegations with new delegations - let juror_info = - JurorInfoOf:: { stake: amount, active_lock, prepare_exit_at: None, delegations }; - >::insert(&who, juror_info); + let jurors_len = Self::do_join_court(&who, amount, sorted_delegations)?; - Self::deposit_event(Event::JurorJoined { juror: who, stake: amount }); + Self::deposit_event(Event::DelegatedToJurors { + delegator: who, + amount, + delegated_jurors: delegations, + }); Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) } @@ -992,8 +946,16 @@ mod pallet { delegations: vec![], }) .delegations; + // future-proof binary search by key + // because many delegators can back one juror + // we might want to fastly find elements later on match delegations.binary_search_by_key(&delegator, |(d, _)| d.clone()) { Ok(i) => { + debug_assert!( + false, + "Delegator {:?} already in delegations.", + delegator + ); delegations[i].1.saturating_add(delegated_stake); } Err(i) => { @@ -1250,6 +1212,68 @@ mod pallet { } } + fn get_valid_delegated_juror( + delegations: &[T::AccountId], + random_number: u128, + ) -> Option { + let jurors: JurorPoolOf = JurorPool::::get(); + let mut delegated_juror = None; + + for count in 0..delegations.len() { + let delegation_index = (random_number.saturating_add(count as u128) + % delegations.len() as u128) as usize; + delegated_juror = match delegations.get(delegation_index) { + Some(del_j) => Some(del_j.clone()), + None => { + log::error!("Delegation with modulo index should exist!"); + debug_assert!(false); + None + } + }; + + if let Some(del_j) = &delegated_juror { + if let Some(delegated_juror_info) = >::get(del_j) { + if Self::get_pool_item(&jurors, delegated_juror_info.stake, del_j).is_some() + { + delegated_juror = Some(del_j.clone()); + break; + } + } + } + } + + delegated_juror + } + + fn add_to_selections( + selections: &mut BTreeMap>, + juror: T::AccountId, + lock_added: BalanceOf, + random_number: u128, + ) { + let delegations = >::get(juror.clone()) + .map_or(vec![], |juror_info| juror_info.delegations.into_inner()); + if delegations.is_empty() { + let sel_add = SelectionAdd::SelfStake { lock: lock_added }; + Self::update_selections(selections, &juror, sel_add); + } else { + let sel_add = SelectionAdd::DelegationWeight; + debug_assert!(!delegations.is_empty()); + + let delegated_juror = + Self::get_valid_delegated_juror(delegations.as_slice(), random_number) + .unwrap_or_else(|| juror.clone()); + + Self::update_selections(selections, &delegated_juror, sel_add); + + let sel_add = SelectionAdd::DelegationStake { + delegated_juror: delegated_juror.clone(), + lock: lock_added, + }; + Self::update_selections(selections, &juror, sel_add); + } + } + // Match the random numbers to select some jurors from the pool. // The active lock (and consumed stake) of the selected jurors // is increased by the random selection weight. @@ -1259,12 +1283,12 @@ mod pallet { selections: &mut BTreeMap>, ) { let mut current_weight = 0u128; + for JurorPoolItem { stake, juror, consumed_stake } in jurors.iter_mut() { let lower_bound = current_weight; let mut unconsumed = stake.saturating_sub(*consumed_stake); let upper_bound = current_weight.saturating_add(unconsumed.saturated_into::()); - let mut selection_count = 0usize; // this always gets the lowest random number first and maybe removes it for random_number in random_set.clone().iter() { @@ -1272,34 +1296,14 @@ mod pallet { let lock_added = unconsumed.min(T::MinJurorStake::get()); unconsumed = unconsumed.saturating_sub(lock_added); - let delegations = >::get(juror.clone()) - .map_or(vec![], |juror_info| juror_info.delegations.into_inner()); - if delegations.is_empty() { - let sel_add = SelectionAdd::SelfStake { lock: lock_added }; - Self::update_selections(selections, juror, sel_add); - } else { - let sel_add = SelectionAdd::DelegationWeight; - debug_assert!(!delegations.is_empty()); - let delegation_index = selection_count % delegations.len(); - let delegated_juror = match delegations.get(delegation_index) { - Some(delegated_juror) => delegated_juror, - None => { - log::error!("Delegation with modulo index should exist!"); - debug_assert!(false); - &juror - } - }; - Self::update_selections(selections, &delegated_juror, sel_add); - - let sel_add = SelectionAdd::DelegationStake { - delegated_juror: delegated_juror.clone(), - lock: lock_added, - }; - Self::update_selections(selections, juror, sel_add); - } + Self::add_to_selections( + selections, + juror.clone(), + lock_added, + *random_number, + ); random_set.remove(random_number); - selection_count = selection_count.saturating_add(1usize); } else { break; } @@ -1546,12 +1550,12 @@ mod pallet { juror ); - let imb = slash_all_delegators(&delegations); + let imb = slash_all_delegators(delegations); total_incentives.subsume(imb); } } None => { - let imb = slash_all_delegators(&delegations); + let imb = slash_all_delegators(delegations); total_incentives.subsume(imb); } } @@ -1908,3 +1912,90 @@ mod pallet { ) } } + +impl Pallet { + fn do_join_court( + who: &T::AccountId, + amount: BalanceOf, + delegations: DelegationsOf, + ) -> Result { + ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); + let free_balance = T::Currency::free_balance(who); + ensure!(amount <= free_balance, Error::::AmountExceedsBalance); + + let mut jurors = JurorPool::::get(); + + let (active_lock, consumed_stake) = if let Some(prev_juror_info) = >::get(who) { + ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); + let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, who) + .ok_or(Error::::JurorNeedsToExit)?; + debug_assert!( + prev_juror_info.prepare_exit_at.is_none(), + "If the pool item is found, the prepare_exit_at could have never been written." + ); + let consumed_stake = pool_item.consumed_stake; + jurors.remove(index); + (prev_juror_info.active_lock, consumed_stake) + } else { + if jurors.is_full() { + let lowest_item = jurors.first(); + let lowest_stake = lowest_item + .map(|pool_item| pool_item.stake) + .unwrap_or_else(>::zero); + debug_assert!({ + let mut sorted = jurors.clone(); + sorted.sort_by_key(|pool_item| (pool_item.stake, pool_item.juror.clone())); + jurors.len() == sorted.len() + && jurors + .iter() + .zip(sorted.iter()) + .all(|(a, b)| lowest_stake <= a.stake && a == b) + }); + ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); + if let Some(pool_item) = lowest_item { + if let Some(mut lowest_juror_info) = >::get(&pool_item.juror) { + let now = >::block_number(); + lowest_juror_info.prepare_exit_at = Some(now); + >::insert(&pool_item.juror, lowest_juror_info); + } + } + // remove the lowest staked juror + jurors.remove(0); + } + (>::zero(), >::zero()) + }; + + match jurors + .binary_search_by_key(&(amount, who), |pool_item| (pool_item.stake, &pool_item.juror)) + { + Ok(_) => { + debug_assert!( + false, + "This should never happen, because we are removing the juror above." + ); + return Err(Error::::JurorTwiceInPool.into()); + } + Err(i) => jurors + .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), consumed_stake }) + .map_err(|_| { + debug_assert!( + false, + "This should never happen, because we are removing the lowest staked \ + juror above." + ); + Error::::MaxJurorsReached + })?, + }; + + T::Currency::set_lock(T::CourtLockId::get(), who, amount, WithdrawReasons::all()); + + let jurors_len = jurors.len() as u32; + JurorPool::::put(jurors); + + let juror_info = + JurorInfoOf:: { stake: amount, active_lock, prepare_exit_at: None, delegations }; + >::insert(who, juror_info); + + Ok(jurors_len) + } +} diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 6ef2fc32c..5f7cc6cfb 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -35,7 +35,7 @@ use zeitgeist_primitives::{ constants::mock::{ AppealBond, BlockHashCount, BlocksPerYear, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, InflationPeriod, MaxAppeals, MaxApprovals, - MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, + MaxDelegations, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, RequestInterval, BASE, }, traits::DisputeResolutionApi, @@ -150,6 +150,7 @@ impl crate::Config for Runtime { type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; + type MaxDelegations = MaxDelegations; type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index af482ab71..dc314cbb1 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -136,7 +136,12 @@ fn put_alice_in_draw(market_id: MarketIdOf, stake: BalanceOf) >::insert(market_id, draws); >::insert( ALICE, - JurorInfo { stake, active_lock: slashable, prepare_exit_at: None }, + JurorInfo { + stake, + active_lock: slashable, + prepare_exit_at: None, + delegations: Default::default(), + }, ); } @@ -203,7 +208,15 @@ fn join_court_successfully_stores_required_data() { System::assert_last_event(Event::JurorJoined { juror: ALICE, stake: amount }.into()); assert_eq!( Jurors::::iter().next().unwrap(), - (ALICE, JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None }) + ( + ALICE, + JurorInfo { + stake: amount, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default() + } + ) ); assert_eq!(Balances::free_balance(ALICE), alice_free_balance_before); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); @@ -227,7 +240,15 @@ fn join_court_works_multiple_joins() { ); assert_eq!( Jurors::::iter().collect::)>>(), - vec![(ALICE, JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None })] + vec![( + ALICE, + JurorInfo { + stake: amount, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default() + } + )] ); assert_ok!(Court::join_court(Origin::signed(BOB), amount)); @@ -242,11 +263,21 @@ fn join_court_works_multiple_joins() { assert_eq!(Jurors::::iter().count(), 2); assert_eq!( Jurors::::get(ALICE).unwrap(), - JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None } + JurorInfo { + stake: amount, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default() + } ); assert_eq!( Jurors::::get(BOB).unwrap(), - JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None } + JurorInfo { + stake: amount, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default() + } ); let higher_amount = amount + 1; @@ -263,11 +294,21 @@ fn join_court_works_multiple_joins() { assert_eq!(Jurors::::iter().count(), 2); assert_eq!( Jurors::::get(BOB).unwrap(), - JurorInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None } + JurorInfo { + stake: amount, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default() + } ); assert_eq!( Jurors::::get(ALICE).unwrap(), - JurorInfo { stake: higher_amount, active_lock: 0u128, prepare_exit_at: None } + JurorInfo { + stake: higher_amount, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default() + } ); }); } @@ -282,7 +323,12 @@ fn join_court_saves_consumed_stake_and_active_lock_for_double_join() { let active_lock = min + 1; Jurors::::insert( ALICE, - JurorInfo { stake: amount, active_lock, prepare_exit_at: None }, + JurorInfo { + stake: amount, + active_lock, + prepare_exit_at: None, + delegations: Default::default(), + }, ); let juror_pool = vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake }]; JurorPool::::put::>(juror_pool.try_into().unwrap()); @@ -504,12 +550,22 @@ fn exit_court_works_with_active_lock() { assert_eq!( >::get(ALICE).unwrap(), - JurorInfo { stake: amount, active_lock: 0, prepare_exit_at: None } + JurorInfo { + stake: amount, + active_lock: 0, + prepare_exit_at: None, + delegations: Default::default() + } ); // assume that `choose_multiple_weighted` has set the active_lock >::insert( ALICE, - JurorInfo { stake: amount, active_lock, prepare_exit_at: None }, + JurorInfo { + stake: amount, + active_lock, + prepare_exit_at: None, + delegations: Default::default(), + }, ); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); @@ -527,7 +583,12 @@ fn exit_court_works_with_active_lock() { ); assert_eq!( Jurors::::get(ALICE).unwrap(), - JurorInfo { stake: active_lock, active_lock, prepare_exit_at: Some(now) } + JurorInfo { + stake: active_lock, + active_lock, + prepare_exit_at: Some(now), + delegations: Default::default() + } ); assert_eq!(Balances::locks(ALICE), vec![the_lock(active_lock)]); }); @@ -593,7 +654,12 @@ fn vote_works() { >::insert(market_id, draws); >::insert( ALICE, - JurorInfo { stake: amount, active_lock: slashable, prepare_exit_at: None }, + JurorInfo { + stake: amount, + active_lock: slashable, + prepare_exit_at: None, + delegations: Default::default(), + }, ); let old_draws = >::get(market_id); @@ -683,7 +749,9 @@ fn vote_fails_if_court_not_found() { salt: ::Hash::default(), }; "denounced" )] -fn vote_fails_if_vote_state_incorrect(vote: crate::Vote<::Hash>) { +fn vote_fails_if_vote_state_incorrect( + vote: crate::Vote<::Hash, crate::DelegatedStakesOf>, +) { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); let market_id = initialize_court(); @@ -771,7 +839,12 @@ fn reveal_vote_works() { >::insert(market_id, draws); >::insert( ALICE, - JurorInfo { stake: amount, active_lock: slashable, prepare_exit_at: None }, + JurorInfo { + stake: amount, + active_lock: slashable, + prepare_exit_at: None, + delegations: Default::default(), + }, ); run_to_block(>::get() + 1); @@ -2494,7 +2567,12 @@ fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { for pool_item in DEFAULT_SET_OF_JURORS.iter() { >::insert( pool_item.juror, - JurorInfo { stake: pool_item.stake, active_lock: 0u128, prepare_exit_at: None }, + JurorInfo { + stake: pool_item.stake, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default(), + }, ); jurors.try_push(pool_item.clone()).unwrap(); } @@ -2550,7 +2628,12 @@ fn random_jurors_returns_a_subset_of_jurors() { for pool_item in DEFAULT_SET_OF_JURORS.iter() { >::insert( pool_item.juror, - JurorInfo { stake: pool_item.stake, active_lock: 0u128, prepare_exit_at: None }, + JurorInfo { + stake: pool_item.stake, + active_lock: 0u128, + prepare_exit_at: None, + delegations: Default::default(), + }, ); jurors.try_push(pool_item.clone()).unwrap(); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 1149a19fe..f2aea192f 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -14,9 +14,9 @@ // // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . - +extern crate alloc; +use alloc::vec::Vec; use zeitgeist_primitives::types::OutcomeReport; - /// The general information about a particular juror. #[derive( parity_scale_codec::Decode, diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 6e73f9d28..9a99f5cbe 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -42,8 +42,8 @@ use zeitgeist_primitives::{ CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, InflationPeriod, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, - MaxCategories, MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, MaxGracePeriod, - MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, + MaxCategories, MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, + MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, @@ -260,6 +260,7 @@ impl zrml_court::Config for Runtime { type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; + type MaxDelegations = MaxDelegations; type MaxDraws = MaxDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; From f4fb44338a841e72a56da426b414e54b4dae4c41 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 13 Apr 2023 15:58:13 +0200 Subject: [PATCH 251/368] fix benchmarks --- zrml/court/src/benchmarks.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index b9af8e408..63e173262 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -101,7 +101,12 @@ where let _ = T::Currency::deposit_creating(&juror, stake); >::insert( juror.clone(), - JurorInfo { stake, active_lock: >::zero(), prepare_exit_at: None }, + JurorInfo { + stake, + active_lock: >::zero(), + prepare_exit_at: None, + delegations: Default::default(), + }, ); let consumed_stake = BalanceOf::::zero(); let pool_item = JurorPoolItem { stake, juror: juror.clone(), consumed_stake }; @@ -157,6 +162,7 @@ where stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, + delegations: Default::default(), }, ); let draw = @@ -277,6 +283,7 @@ benchmarks! { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, + delegations: Default::default(), }); let denounced_juror_unlookup = T::Lookup::unlookup(denounced_juror.clone()); let commitment = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); @@ -314,6 +321,7 @@ benchmarks! { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, + delegations: Default::default(), }); let commitment = T::Hashing::hash_of(&(caller.clone(), outcome.clone(), salt)); @@ -375,6 +383,7 @@ benchmarks! { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, + delegations: Default::default(), }); let outcome = OutcomeReport::Scalar(i as u128); let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); @@ -431,6 +440,7 @@ benchmarks! { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, + delegations: Default::default(), }); let outcome = winner_outcome.clone(); let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); From a0ac03da306ae45101360849165c5f78d8ecc376 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 14 Apr 2023 13:36:29 +0200 Subject: [PATCH 252/368] optimize logic --- primitives/src/constants/mock.rs | 2 +- runtime/battery-station/src/parameters.rs | 2 +- runtime/common/src/lib.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/court/src/benchmarks.rs | 50 ++- zrml/court/src/lib.rs | 398 ++++++++++++---------- zrml/court/src/mock.rs | 6 +- zrml/court/src/tests.rs | 120 +++---- zrml/court/src/types.rs | 14 +- zrml/court/src/weights.rs | 40 +-- zrml/prediction-markets/src/mock.rs | 14 +- zrml/prediction-markets/src/tests.rs | 14 +- 12 files changed, 345 insertions(+), 319 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index fdd14d3c1..2792ee922 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -45,7 +45,7 @@ parameter_types! { pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const MaxAppeals: u32 = 4; pub const MaxDelegations: u32 = 5; - pub const MaxDraws: u32 = 47; + pub const MaxSelectedDraws: u32 = 47; pub const MaxJurors: u32 = 1_000; pub const MinJurorStake: Balance = 50 * CENT; pub const InflationPeriod: BlockNumber = 20; diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index f3e615427..ec13a50cc 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -100,7 +100,7 @@ parameter_types! { /// The maximum number of delegations per juror account. pub const MaxDelegations: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDraws: u32 = 47; + pub const MaxSelectedDraws: u32 = 47; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 3df32e47c..6c6f8b4ab 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -960,7 +960,7 @@ macro_rules! impl_config_traits { type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDelegations = MaxDelegations; - type MaxDraws = MaxDraws; + type MaxSelectedDraws = MaxSelectedDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index c234e9164..5bed20760 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -100,7 +100,7 @@ parameter_types! { /// The maximum number of delegations per juror account. pub const MaxDelegations: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxDraws: u32 = 47; + pub const MaxSelectedDraws: u32 = 47; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 63e173262..d99341f12 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -25,8 +25,8 @@ extern crate alloc; use crate::{ types::{CourtStatus, Draw, JurorInfo, JurorPoolItem, Vote}, - AppealInfo, BalanceOf, Call, Config, Courts, Draws, JurorPool, Jurors, MarketOf, - Pallet as Court, Pallet, RequestBlock, + AppealInfo, BalanceOf, Call, Config, Courts, JurorPool, Jurors, MarketOf, Pallet as Court, + Pallet, RequestBlock, SelectedDraws, }; use alloc::vec; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; @@ -151,8 +151,8 @@ where T: Config, { // remove last random selections of on_dispute - >::remove(market_id); - let mut draws = >::get(market_id); + >::remove(market_id); + let mut draws = >::get(market_id); for i in 0..number { let juror = account("juror", i, 0); deposit::(&juror); @@ -169,7 +169,7 @@ where Draw { juror, vote: Vote::Drawn, weight: 1u32, slashable: T::MinJurorStake::get() }; draws.try_push(draw).unwrap(); } - >::insert(market_id, draws); + >::insert(market_id, draws); Ok(()) } @@ -196,10 +196,6 @@ benchmarks! { }: _(RawOrigin::Signed(caller)) exit_court_remove { - let j in 0..(T::MaxJurors::get() - 1); - - fill_pool::(j)?; - let caller: T::AccountId = whitelisted_caller(); join_with_min_stake::(&caller)?; @@ -215,10 +211,6 @@ benchmarks! { }: exit_court(RawOrigin::Signed(caller), juror) exit_court_set { - let j in 0..(T::MaxJurors::get() - 1); - - fill_pool::(j)?; - let caller: T::AccountId = whitelisted_caller(); join_with_min_stake::(&caller)?; @@ -234,7 +226,7 @@ benchmarks! { }: exit_court(RawOrigin::Signed(caller), juror) vote { - let d in 1..T::MaxDraws::get(); + let d in 1..T::MaxSelectedDraws::get(); fill_pool::(T::MaxJurors::get() - 1)?; @@ -246,7 +238,7 @@ benchmarks! { fill_draws::(market_id, d)?; - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); let draws_len = draws.len(); draws[draws_len.saturating_sub(1usize)] = Draw { juror: caller.clone(), @@ -254,7 +246,7 @@ benchmarks! { weight: 1u32, slashable: >::zero() }; - >::insert(market_id, draws); + >::insert(market_id, draws); >::set_block_number(pre_vote + 1u64.saturated_into::()); @@ -262,7 +254,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller), market_id, commitment_vote) denounce_vote { - let d in 1..T::MaxDraws::get(); + let d in 1..T::MaxSelectedDraws::get(); let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); fill_pool::(necessary_jurors_weight as u32)?; @@ -288,7 +280,7 @@ benchmarks! { let denounced_juror_unlookup = T::Lookup::unlookup(denounced_juror.clone()); let commitment = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); let draws_len = draws.len(); draws[draws_len.saturating_sub(1usize)] = Draw { juror: denounced_juror, @@ -296,13 +288,13 @@ benchmarks! { weight: 1u32, slashable: T::MinJurorStake::get(), }; - >::insert(market_id, draws); + >::insert(market_id, draws); >::set_block_number(pre_vote + 1u64.saturated_into::()); }: _(RawOrigin::Signed(caller), market_id, denounced_juror_unlookup, outcome, salt) reveal_vote { - let d in 1..T::MaxDraws::get(); + let d in 1..T::MaxSelectedDraws::get(); fill_pool::(T::MaxJurors::get() - 1)?; @@ -325,7 +317,7 @@ benchmarks! { }); let commitment = T::Hashing::hash_of(&(caller.clone(), outcome.clone(), salt)); - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); let draws_len = draws.len(); draws[draws_len.saturating_sub(1usize)] = Draw { juror: caller.clone(), @@ -333,7 +325,7 @@ benchmarks! { weight: 1u32, slashable: T::MinJurorStake::get(), }; - >::insert(market_id, draws); + >::insert(market_id, draws); >::set_block_number(vote_end + 1u64.saturated_into::()); }: _(RawOrigin::Signed(caller), market_id, outcome, salt) @@ -374,8 +366,8 @@ benchmarks! { let salt = Default::default(); // remove last random selections of on_dispute - >::remove(market_id); - let mut draws = >::get(market_id); + >::remove(market_id); + let mut draws = >::get(market_id); let draws_len = Court::::necessary_jurors_weight(a as usize) as u32; for i in 0..draws_len { let juror: T::AccountId = account("juror", i, 0); @@ -396,7 +388,7 @@ benchmarks! { }; draws.try_push(draw).unwrap(); } - >::insert(market_id, draws); + >::insert(market_id, draws); >::set_block_number(aggregation + 1u64.saturated_into::()); let now = >::block_number(); @@ -417,7 +409,7 @@ benchmarks! { } reassign_juror_stakes { - let d in 1..T::MaxDraws::get(); + let d in 1..T::MaxSelectedDraws::get(); let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); fill_pool::(necessary_jurors_weight as u32)?; @@ -432,8 +424,8 @@ benchmarks! { let salt = Default::default(); // remove last random selections of on_dispute - >::remove(market_id); - let mut draws = >::get(market_id); + >::remove(market_id); + let mut draws = >::get(market_id); for i in 0..d { let juror: T::AccountId = account("juror", i, 0); >::insert(&juror, JurorInfo { @@ -453,7 +445,7 @@ benchmarks! { }; draws.try_push(draw).unwrap(); } - >::insert(market_id, draws); + >::insert(market_id, draws); }: _(RawOrigin::Signed(caller), market_id) set_inflation { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 4287669fa..bd6ad60a5 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -25,11 +25,10 @@ extern crate alloc; use crate::{ weights::WeightInfoZeitgeist, AppealInfo, CommitmentMatcher, CourtInfo, CourtStatus, Draw, JurorInfo, JurorPoolItem, JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, - SelectionValue, SelfInfo, Vote, + SelectionError, SelectionValue, SelfInfo, Vote, }; use alloc::{ collections::{BTreeMap, BTreeSet}, - vec, vec::Vec, }; use core::marker::PhantomData; @@ -145,7 +144,7 @@ mod pallet { /// The maximum number of randomly selected jurors for a dispute. #[pallet::constant] - type MaxDraws: Get; + type MaxSelectedDraws: Get; #[pallet::constant] type MaxDelegations: Get; @@ -217,12 +216,13 @@ mod pallet { pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; pub(crate) type DrawOf = Draw, BalanceOf, HashOf, DelegatedStakesOf>; - pub(crate) type DrawsOf = BoundedVec, ::MaxDraws>; + pub(crate) type SelectedDrawsOf = BoundedVec, ::MaxSelectedDraws>; pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; pub(crate) type CommitmentMatcherOf = CommitmentMatcher, HashOf>; pub(crate) type RawCommitmentOf = RawCommitment, HashOf>; pub type CacheSize = ConstU32<64>; + #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); @@ -243,8 +243,8 @@ mod pallet { /// The randomly selected jurors with their vote. #[pallet::storage] - pub type Draws = - StorageMap<_, Blake2_128Concat, MarketIdOf, DrawsOf, ValueQuery>; + pub type SelectedDraws = + StorageMap<_, Blake2_128Concat, MarketIdOf, SelectedDrawsOf, ValueQuery>; /// The general information about each court. #[pallet::storage] @@ -366,9 +366,7 @@ mod pallet { MaxJurorsReached, /// In order to exit the court the juror has to exit /// the pool first with `prepare_exit_court`. - JurorNotPreparedToExit, - /// The juror was not found in the pool. - JurorAlreadyPreparedToExit, + JurorAlreadyPreparedExit, /// The juror needs to exit the court and then rejoin. JurorNeedsToExit, /// The juror was not randomly selected for the court. @@ -398,7 +396,7 @@ mod pallet { /// This should not happen, because the juror account should only be once in a pool. JurorTwiceInPool, /// The caller of this function is not part of the juror draws. - CallerNotInDraws, + CallerNotInSelectedDraws, /// The callers balance is lower than the appeal bond. AppealBondExceedsBalance, /// The outcome does not match the market outcome type. @@ -412,6 +410,8 @@ mod pallet { JurorDelegated, SelfDelegationNotAllowed, IdenticalDelegationsNotAllowed, + NoDelegations, + DelegatedToInvalidJuror, } #[pallet::hooks] @@ -456,13 +456,14 @@ mod pallet { ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let jurors_len = Self::do_join_court(&who, amount, Default::default())?; + let jurors_len = Self::do_join_court(&who, amount, None)?; Self::deposit_event(Event::JurorJoined { juror: who, stake: amount }); Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) } + // TODO benchmark #[pallet::weight(T::WeightInfo::join_court(T::MaxJurors::get()))] #[transactional] pub fn delegate( @@ -472,8 +473,19 @@ mod pallet { ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; + ensure!(!delegations.is_empty(), Error::::NoDelegations); let mut sorted_delegations: DelegationsOf = delegations.clone().try_into().map_err(|_| Error::::MaxDelegationsReached)?; + + let jurors = JurorPool::::get(); + let is_valid_set = sorted_delegations.iter().all(|pretended_juror| { + >::get(pretended_juror).map_or(false, |pretended_juror_info| { + Self::get_pool_item(&jurors, pretended_juror_info.stake, pretended_juror) + .is_some() + && pretended_juror_info.delegations.is_none() + }) + }); + ensure!(is_valid_set, Error::::DelegatedToInvalidJuror); // ensure all elements are different sorted_delegations.sort(); let has_duplicates = sorted_delegations @@ -483,7 +495,7 @@ mod pallet { ensure!(!has_duplicates, Error::::IdenticalDelegationsNotAllowed); ensure!(!sorted_delegations.contains(&who), Error::::SelfDelegationNotAllowed); - let jurors_len = Self::do_join_court(&who, amount, sorted_delegations)?; + let jurors_len = Self::do_join_court(&who, amount, Some(sorted_delegations))?; Self::deposit_event(Event::DelegatedToJurors { delegator: who, @@ -498,7 +510,6 @@ mod pallet { /// When this is called the juror is not anymore able to get drawn for new cases. /// The juror gets removed from the stake-weighted pool. /// After that the juror can exit the court. - /// Returns an error if the juror is already not part of the pool anymore. /// /// # Weight /// @@ -510,16 +521,19 @@ mod pallet { let mut prev_juror_info = >::get(&who).ok_or(Error::::JurorDoesNotExist)?; + ensure!( + prev_juror_info.prepare_exit_at.is_none(), + Error::::JurorAlreadyPreparedExit + ); let mut jurors = JurorPool::::get(); let jurors_len = jurors.len() as u32; + + // do not error in the else case + // because the juror might have been already removed from the pool if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) { jurors.remove(index); >::put(jurors); - } else { - // this error can happen if the lowest bonded juror was removed - // or if the current extrinsic was already called before - return Err(Error::::JurorAlreadyPreparedToExit.into()); } let now = >::block_number(); @@ -543,10 +557,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight( - T::WeightInfo::exit_court_set(T::MaxJurors::get()) - .max(T::WeightInfo::exit_court_remove(T::MaxJurors::get())) - )] + #[pallet::weight(T::WeightInfo::exit_court_set().max(T::WeightInfo::exit_court_remove()))] #[transactional] pub fn exit_court( origin: OriginFor, @@ -559,13 +570,6 @@ mod pallet { let mut prev_juror_info = >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; - let jurors = JurorPool::::get(); - let jurors_len = jurors.len() as u32; - ensure!( - Self::get_pool_item(&jurors, prev_juror_info.stake, &juror).is_none(), - Error::::JurorNotPreparedToExit - ); - let now = >::block_number(); let prepare_exit_at = prev_juror_info.prepare_exit_at.ok_or(Error::::PrepareExitAtNotPresent)?; @@ -577,11 +581,7 @@ mod pallet { let (exit_amount, active_lock, weight) = if prev_juror_info.active_lock.is_zero() { T::Currency::remove_lock(T::CourtLockId::get(), &juror); Jurors::::remove(&juror); - ( - prev_juror_info.stake, - >::zero(), - T::WeightInfo::exit_court_remove(jurors_len), - ) + (prev_juror_info.stake, >::zero(), T::WeightInfo::exit_court_remove()) } else { let active_lock = prev_juror_info.active_lock; let exit_amount = prev_juror_info.stake.saturating_sub(active_lock); @@ -595,7 +595,7 @@ mod pallet { prev_juror_info.stake = active_lock; Jurors::::insert(&juror, prev_juror_info); - (exit_amount, active_lock, T::WeightInfo::exit_court_set(jurors_len)) + (exit_amount, active_lock, T::WeightInfo::exit_court_set()) }; Self::deposit_event(Event::JurorExited { juror, exit_amount, active_lock }); @@ -614,7 +614,7 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors /// in the list of random selections (draws). - #[pallet::weight(T::WeightInfo::vote(T::MaxDraws::get()))] + #[pallet::weight(T::WeightInfo::vote(T::MaxSelectedDraws::get()))] #[transactional] pub fn vote( origin: OriginFor, @@ -630,25 +630,27 @@ mod pallet { Error::::NotInVotingPeriod ); - let mut draws = >::get(market_id); - let (index, draw) = match draws.iter().position(|draw| draw.juror == who) { - Some(index) => { + let mut draws = >::get(market_id); + + match draws.binary_search_by_key(&who, |draw| draw.juror.clone()) { + Ok(index) => { + let draw = draws[index].clone(); + // allow to override last vote ensure!( matches!(draws[index].vote, Vote::Drawn | Vote::Secret { commitment: _ }), Error::::InvalidVoteState ); - (index, draws[index].clone()) - } - None => return Err(Error::::CallerNotInDraws.into()), - }; - let vote = Vote::Secret { commitment: commitment_vote }; - draws[index] = Draw { juror: who.clone(), vote, ..draw }; + let vote = Vote::Secret { commitment: commitment_vote }; + draws[index] = Draw { vote, ..draw }; + } + Err(_) => return Err(Error::::CallerNotInSelectedDraws.into()), + } let draws_len = draws.len() as u32; - >::insert(market_id, draws); + >::insert(market_id, draws); Self::deposit_event(Event::JurorVoted { juror: who, @@ -676,7 +678,7 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors /// in the list of random selections (draws). - #[pallet::weight(T::WeightInfo::denounce_vote(T::MaxDraws::get()))] + #[pallet::weight(T::WeightInfo::denounce_vote(T::MaxSelectedDraws::get()))] #[transactional] pub fn denounce_vote( origin: OriginFor, @@ -704,22 +706,26 @@ mod pallet { Error::::NotInVotingPeriod ); - let mut draws = >::get(market_id); - let draws_len = draws.len() as u32; - let (index, draw) = match draws.iter().position(|draw| draw.juror == juror) { - Some(index) => (index, draws[index].clone()), - None => return Err(Error::::JurorNotDrawn.into()), - }; + let mut draws = >::get(market_id); + match draws.binary_search_by_key(&juror, |draw| draw.juror.clone()) { + Ok(index) => { + let draw = draws[index].clone(); - let raw_commmitment = - RawCommitment { juror: juror.clone(), outcome: outcome.clone(), salt }; + let raw_commmitment = + RawCommitment { juror: juror.clone(), outcome: outcome.clone(), salt }; - let commitment = Self::get_hashed_commitment(draw.vote, raw_commmitment)?; + let commitment = Self::get_hashed_commitment(draw.vote, raw_commmitment)?; - // slash for the misbehaviour happens in reassign_juror_stakes - let raw_vote = Vote::Denounced { commitment, outcome: outcome.clone(), salt }; - draws[index] = Draw { juror: juror.clone(), vote: raw_vote, ..draw }; - >::insert(market_id, draws); + // slash for the misbehaviour happens in reassign_juror_stakes + let raw_vote = Vote::Denounced { commitment, outcome: outcome.clone(), salt }; + draws[index] = Draw { vote: raw_vote, ..draw }; + } + Err(_) => return Err(Error::::JurorNotDrawn.into()), + } + + let draws_len = draws.len() as u32; + + >::insert(market_id, draws); Self::deposit_event(Event::DenouncedJurorVote { denouncer, @@ -745,7 +751,7 @@ mod pallet { /// /// Complexity: `O(n)`, where `n` is the number of jurors /// in the list of random selections (draws). - #[pallet::weight(T::WeightInfo::reveal_vote(T::MaxDraws::get()))] + #[pallet::weight(T::WeightInfo::reveal_vote(T::MaxSelectedDraws::get()))] #[transactional] pub fn reveal_vote( origin: OriginFor, @@ -766,21 +772,25 @@ mod pallet { Error::::NotInAggregationPeriod ); - let mut draws = >::get(market_id); - let draws_len = draws.len() as u32; - let (index, draw) = match draws.iter().position(|draw| draw.juror == who) { - Some(index) => (index, draws[index].clone()), - None => return Err(Error::::JurorNotDrawn.into()), - }; + let mut draws = >::get(market_id); + match draws.binary_search_by_key(&who, |draw| draw.juror.clone()) { + Ok(index) => { + let draw = draws[index].clone(); + + let raw_commitment = + RawCommitment { juror: who.clone(), outcome: outcome.clone(), salt }; + + let commitment = Self::get_hashed_commitment(draw.vote, raw_commitment)?; - let raw_commitment = - RawCommitment { juror: who.clone(), outcome: outcome.clone(), salt }; + let raw_vote = Vote::Revealed { commitment, outcome: outcome.clone(), salt }; + draws[index] = Draw { juror: who.clone(), vote: raw_vote, ..draw }; + } + Err(_) => return Err(Error::::JurorNotDrawn.into()), + } - let commitment = Self::get_hashed_commitment(draw.vote, raw_commitment)?; + let draws_len = draws.len() as u32; - let raw_vote = Vote::Revealed { commitment, outcome: outcome.clone(), salt }; - draws[index] = Draw { juror: who.clone(), vote: raw_vote, ..draw }; - >::insert(market_id, draws); + >::insert(market_id, draws); Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); @@ -802,6 +812,7 @@ mod pallet { T::MaxJurors::get(), T::MaxAppeals::get(), CacheSize::get(), + CacheSize::get(), ))] #[transactional] pub fn appeal( @@ -819,7 +830,7 @@ mod pallet { Self::check_appealable_market(&market_id, &court, now)?; // the outcome which would be resolved on is appealed (including oracle report) - let old_draws = Draws::::get(market_id); + let old_draws = SelectedDraws::::get(market_id); let appealed_outcome = Self::get_latest_resolved_outcome(&market_id, old_draws.as_slice())?; let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; @@ -833,6 +844,8 @@ mod pallet { // used for benchmarking, juror pool is queried inside `select_jurors` let jurors_len = >::decode_len().unwrap_or(0) as u32; + let mut ids_len_1 = 0u32; + // if appeal_number == MaxAppeals, then don't start a new appeal round if appeal_number < T::MaxAppeals::get() as usize { let new_draws = Self::select_jurors(appeal_number)?; @@ -848,10 +861,8 @@ mod pallet { court.update_lifecycle(round_timing); let new_resolve_at = court.cycle_ends.appeal; debug_assert!(new_resolve_at != last_resolve_at); - // TODO benchmark component missing - let _ids_len_1 = - T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; - >::insert(market_id, new_draws); + ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; + >::insert(market_id, new_draws); Self::unlock_jurors_from_last_draw(&market_id, old_draws); } @@ -864,7 +875,7 @@ mod pallet { let appeal_number = appeal_number as u32; Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); - Ok(Some(T::WeightInfo::appeal(jurors_len, appeal_number, ids_len_0)).into()) + Ok(Some(T::WeightInfo::appeal(jurors_len, appeal_number, ids_len_0, ids_len_1)).into()) } /// The juror stakes get reassigned according to the plurality decision of the jurors. @@ -878,7 +889,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(n)`, where `n` is the number of randomly selected jurors for this court. - #[pallet::weight(T::WeightInfo::reassign_juror_stakes(T::MaxDraws::get()))] + #[pallet::weight(T::WeightInfo::reassign_juror_stakes(T::MaxSelectedDraws::get()))] #[transactional] pub fn reassign_juror_stakes( origin: OriginFor, @@ -893,7 +904,7 @@ mod pallet { _ => return Err(Error::::CourtNotClosed.into()), }; - let draws = Draws::::get(market_id); + let draws = SelectedDraws::::get(market_id); let draws_len = draws.len() as u32; let reward_pot = Self::reward_pot(&market_id); @@ -931,21 +942,17 @@ mod pallet { slash_juror(&draw.juror, draw.slashable); } Vote::Revealed { commitment: _, outcome, salt: _ } => { - jurors_to_stakes - .entry(draw.juror) - .or_insert(JurorVoteWithStakes { self_info: None, delegations: vec![] }) - .self_info = Some(SelfInfo { slashable: draw.slashable, outcome }); + jurors_to_stakes.entry(draw.juror).or_default().self_info = + Some(SelfInfo { slashable: draw.slashable, outcome }); } Vote::Delegated { delegated_stakes } => { let delegator = draw.juror; for (j, delegated_stake) in delegated_stakes { - let delegations = &mut jurors_to_stakes - .entry(j) - .or_insert(JurorVoteWithStakes { - self_info: None, - delegations: vec![], - }) - .delegations; + // fill the delegations for each juror + // [(juror_0, [(delegator_0, delegator_stake_0), ...]), + // (juror_1, [(delegator_42, delegator_stake_42), ...]), ...] + let delegations = + &mut jurors_to_stakes.entry(j).or_default().delegations; // future-proof binary search by key // because many delegators can back one juror // we might want to fastly find elements later on @@ -972,7 +979,7 @@ mod pallet { court.status = CourtStatus::Reassigned; >::insert(market_id, court); - >::remove(market_id); + >::remove(market_id); Self::deposit_event(Event::JurorStakesReassigned { market_id }); @@ -1068,7 +1075,7 @@ mod pallet { // Get the sum of the unconsumed stake from all jurors in the pool. // The unconsumed stake is the stake that was not already locked in previous courts. - fn get_unconsumed_stake(jurors: &JurorPoolOf) -> u128 { + fn get_unconsumed_stake_sum(jurors: &[JurorPoolItemOf]) -> u128 { jurors .iter() .map(|pool_item| { @@ -1116,16 +1123,16 @@ mod pallet { } fn add_delegated_juror( - delegated_stakes: &mut DelegatedStakesOf, + mut delegated_stakes: DelegatedStakesOf, delegated_juror: &T::AccountId, amount: BalanceOf, - ) { + ) -> DelegatedStakesOf { match delegated_stakes.binary_search_by_key(&delegated_juror, |(j, _)| j) { Ok(index) => { - (*delegated_stakes)[index].1 = delegated_stakes[index].1.saturating_add(amount); + delegated_stakes[index].1 = delegated_stakes[index].1.saturating_add(amount); } Err(index) => { - let _ = (*delegated_stakes) + let _ = delegated_stakes .try_insert(index, (delegated_juror.clone(), amount)) .map_err(|_| { debug_assert!( @@ -1136,6 +1143,8 @@ mod pallet { }); } } + + delegated_stakes } // Updates the `selections` map for the juror and the lock amount. @@ -1158,20 +1167,10 @@ mod pallet { } SelectionAdd::DelegationStake { delegated_juror, lock } => { *slashable = slashable.saturating_add(lock); - let delegated_stakes_before = delegated_stakes.clone(); - Self::add_delegated_juror(delegated_stakes, &delegated_juror, lock); - debug_assert!( - delegated_stakes_before - .iter() - .find(|(j, _)| j == &delegated_juror) - .map_or(>::zero(), |(_, amount)| *amount) - + lock - == delegated_stakes - .iter() - .find(|(j, _)| j == &delegated_juror) - .map(|(_, amount)| *amount) - .unwrap(), - "Delegated juror stake should contain the updated amount." + *delegated_stakes = Self::add_delegated_juror( + delegated_stakes.clone(), + &delegated_juror, + lock, ); } SelectionAdd::DelegationWeight => { @@ -1191,8 +1190,11 @@ mod pallet { ); } SelectionAdd::DelegationStake { delegated_juror, lock } => { - let mut delegated_stakes = DelegatedStakesOf::::default(); - Self::add_delegated_juror(&mut delegated_stakes, &delegated_juror, lock); + let delegated_stakes = Self::add_delegated_juror( + DelegatedStakesOf::::default(), + &delegated_juror, + lock, + ); selections.insert( juror.clone(), SelectionValue { weight: 0, slashable: lock, delegated_stakes }, @@ -1233,6 +1235,10 @@ mod pallet { if let Some(del_j) = &delegated_juror { if let Some(delegated_juror_info) = >::get(del_j) { + if delegated_juror_info.delegations.is_some() { + // skip if delegated juror is delegator herself + continue; + } if Self::get_pool_item(&jurors, delegated_juror_info.stake, del_j).is_some() { delegated_juror = Some(del_j.clone()); @@ -1247,61 +1253,78 @@ mod pallet { fn add_to_selections( selections: &mut BTreeMap>, - juror: T::AccountId, + juror: &T::AccountId, lock_added: BalanceOf, random_number: u128, - ) { - let delegations = >::get(juror.clone()) - .map_or(vec![], |juror_info| juror_info.delegations.into_inner()); - if delegations.is_empty() { - let sel_add = SelectionAdd::SelfStake { lock: lock_added }; - Self::update_selections(selections, &juror, sel_add); - } else { - let sel_add = SelectionAdd::DelegationWeight; - debug_assert!(!delegations.is_empty()); - - let delegated_juror = - Self::get_valid_delegated_juror(delegations.as_slice(), random_number) - .unwrap_or_else(|| juror.clone()); - - Self::update_selections(selections, &delegated_juror, sel_add); - - let sel_add = SelectionAdd::DelegationStake { - delegated_juror: delegated_juror.clone(), - lock: lock_added, - }; - Self::update_selections(selections, &juror, sel_add); + ) -> Result<(), SelectionError> { + let delegations_opt = + >::get(juror.clone()).and_then(|juror_info| juror_info.delegations); + match delegations_opt { + Some(delegations) => { + let delegated_juror = + Self::get_valid_delegated_juror(delegations.as_slice(), random_number) + .ok_or(SelectionError::NoValidDelegatedJuror)?; + + // delegated juror gets the vote weight + let sel_add = SelectionAdd::DelegationWeight; + Self::update_selections(selections, &delegated_juror, sel_add); + + let sel_add = SelectionAdd::DelegationStake { + delegated_juror: delegated_juror.clone(), + lock: lock_added, + }; + // delegator risks his stake (to delegated juror), but gets no vote weight + Self::update_selections(selections, juror, sel_add); + } + None => { + let sel_add = SelectionAdd::SelfStake { lock: lock_added }; + Self::update_selections(selections, juror, sel_add); + } } + + Ok(()) } // Match the random numbers to select some jurors from the pool. // The active lock (and consumed stake) of the selected jurors // is increased by the random selection weight. - fn process_juror_pool( + fn get_selections( jurors: &mut JurorPoolOf, - random_set: &mut BTreeSet, - selections: &mut BTreeMap>, - ) { - let mut current_weight = 0u128; + mut random_set: BTreeSet, + ) -> BTreeMap> { + let mut selections = BTreeMap::>::new(); - for JurorPoolItem { stake, juror, consumed_stake } in jurors.iter_mut() { + let mut invalid_juror_indices = Vec::::new(); + + let mut current_weight = 0u128; + for (i, pool_item) in jurors.iter_mut().enumerate() { let lower_bound = current_weight; - let mut unconsumed = stake.saturating_sub(*consumed_stake); + let mut unconsumed = pool_item.stake.saturating_sub(pool_item.consumed_stake); let upper_bound = current_weight.saturating_add(unconsumed.saturated_into::()); // this always gets the lowest random number first and maybe removes it - for random_number in random_set.clone().iter() { + let unchangable_random_numbers = random_set.clone(); + for random_number in unchangable_random_numbers.iter() { if Self::in_range(*random_number, lower_bound, upper_bound) { let lock_added = unconsumed.min(T::MinJurorStake::get()); unconsumed = unconsumed.saturating_sub(lock_added); - Self::add_to_selections( - selections, - juror.clone(), + match Self::add_to_selections( + &mut selections, + &pool_item.juror, lock_added, *random_number, - ); + ) { + Ok(()) => {} + Err(SelectionError::NoValidDelegatedJuror) => { + // it would be pretty expensive to request another selection + // so just ignore this missing MinJurorStake + // I mean we also miss MinJurorStake in the case + // if the juror fails to vote or reveal or gets denounced + invalid_juror_indices.push(i); + } + } random_set.remove(random_number); } else { @@ -1309,8 +1332,9 @@ mod pallet { } } - let total_lock_added = Self::update_active_lock(juror, selections); - *consumed_stake = consumed_stake.saturating_add(total_lock_added); + let total_lock_added = Self::update_active_lock(&pool_item.juror, &selections); + pool_item.consumed_stake = + pool_item.consumed_stake.saturating_add(total_lock_added); if random_set.is_empty() { break; @@ -1318,6 +1342,12 @@ mod pallet { current_weight = upper_bound; } + + for i in invalid_juror_indices { + jurors.remove(i); + } + + selections } // Converts the `selections` map into a vector of `Draw` structs. @@ -1347,21 +1377,20 @@ mod pallet { // according to the weighted stake of the jurors. // Return the random draws. pub(crate) fn choose_multiple_weighted( - jurors: &mut JurorPoolOf, number: usize, ) -> Result>, DispatchError> { - let total_weight = Self::get_unconsumed_stake(jurors); - let mut random_set = Self::get_n_random_numbers(number, total_weight)?; - let mut selections = BTreeMap::>::new(); - - Self::process_juror_pool(jurors, &mut random_set, &mut selections); + let mut jurors = >::get(); + let total_weight = Self::get_unconsumed_stake_sum(jurors.as_slice()); + let random_set = Self::get_n_random_numbers(number, total_weight)?; + let selections = Self::get_selections(&mut jurors, random_set); + >::put(jurors); Ok(Self::convert_selections_to_draws(selections)) } // Reduce the active lock of the jurors from the last draw. // This is useful so that the jurors can thaw their non-locked stake. - fn unlock_jurors_from_last_draw(market_id: &MarketIdOf, last_draws: DrawsOf) { + fn unlock_jurors_from_last_draw(market_id: &MarketIdOf, last_draws: SelectedDrawsOf) { // keep in mind that the old draw likely contains different jurors for old_draw in last_draws { if let Some(mut juror_info) = >::get(&old_draw.juror) { @@ -1380,6 +1409,7 @@ mod pallet { } } + // TODO benchmark select_jurors explicitly for future comparisons // Selects the jurors for the next round. // The `consumed_stake` in `JurorPool` and `active_lock` in `Jurors` is increased // equally according to the weight inside the `new_draws`. @@ -1387,25 +1417,28 @@ mod pallet { // in further court rounds shrinks. // // Returns the new draws. - pub(crate) fn select_jurors(appeal_number: usize) -> Result, DispatchError> { - let mut jurors: JurorPoolOf = JurorPool::::get(); + pub(crate) fn select_jurors( + appeal_number: usize, + ) -> Result, DispatchError> { + let jurors_len = >::decode_len().unwrap_or(0); let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); - ensure!(jurors.len() >= necessary_jurors_weight, Error::::NotEnoughJurors); + ensure!(jurors_len >= necessary_jurors_weight, Error::::NotEnoughJurors); - let random_jurors = - Self::choose_multiple_weighted(&mut jurors, necessary_jurors_weight)?; + let random_jurors = Self::choose_multiple_weighted(necessary_jurors_weight)?; + debug_assert!( + random_jurors.windows(2).all(|window| window[0].juror <= window[1].juror), + "The vector is not sorted by the juror field" + ); debug_assert!( - random_jurors.len() <= T::MaxDraws::get() as usize, + random_jurors.len() <= T::MaxSelectedDraws::get() as usize, "The number of randomly selected jurors should be less than or equal to \ - `MaxDraws`." + `MaxSelectedDraws`." ); - let new_draws = >::truncate_from(random_jurors); - // new appeal round should have a fresh set of draws - // modified consumed_stake for each selected juror - >::put(jurors); - Ok(new_draws) + // TODO what is the maximum number of draws with delegations? It is using necessary_jurors_weight (the number of atoms / draw weight) for the last round + // new appeal round should have a fresh set of draws + Ok(>::truncate_from(random_jurors)) } // Returns (index, pool_item) if the pool item is part of the juror pool. @@ -1510,7 +1543,7 @@ mod pallet { let mut total_incentives = >::zero(); let slash_all_delegators = - |delegations: &Vec<(T::AccountId, BalanceOf)>| -> NegativeImbalanceOf { + |delegations: &[(T::AccountId, BalanceOf)]| -> NegativeImbalanceOf { let mut total_imb = >::zero(); for (delegator, d_slashable) in delegations.iter() { let (imb, missing) = T::Currency::slash(delegator, *d_slashable); @@ -1550,12 +1583,14 @@ mod pallet { juror ); - let imb = slash_all_delegators(delegations); + let imb = slash_all_delegators(delegations.as_slice()); total_incentives.subsume(imb); } } None => { - let imb = slash_all_delegators(delegations); + // in this case the delegators have delegated their vote + // to a tardy or denounced juror + let imb = slash_all_delegators(delegations.as_slice()); total_incentives.subsume(imb); } } @@ -1740,7 +1775,7 @@ mod pallet { let _ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.cycle_ends.appeal)?; - >::insert(market_id, new_draws); + >::insert(market_id, new_draws); >::insert(market_id, court); Ok(()) @@ -1756,7 +1791,7 @@ mod pallet { ); let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let draws = Draws::::get(market_id); + let draws = SelectedDraws::::get(market_id); let resolved_outcome = Self::get_latest_resolved_outcome(market_id, draws.as_slice())?; Self::unlock_jurors_from_last_draw(market_id, draws); court.status = CourtStatus::Closed { winner: resolved_outcome.clone() }; @@ -1877,9 +1912,9 @@ mod pallet { }) .collect::>>(); - let old_draws = Draws::::get(market_id); + let old_draws = SelectedDraws::::get(market_id); Self::unlock_jurors_from_last_draw(market_id, old_draws); - >::remove(market_id); + >::remove(market_id); >::remove(market_id); Ok(gd_outcomes) @@ -1891,9 +1926,9 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let old_draws = Draws::::get(market_id); + let old_draws = SelectedDraws::::get(market_id); Self::unlock_jurors_from_last_draw(market_id, old_draws); - >::remove(market_id); + >::remove(market_id); >::remove(market_id); Ok(()) @@ -1917,7 +1952,7 @@ impl Pallet { fn do_join_court( who: &T::AccountId, amount: BalanceOf, - delegations: DelegationsOf, + delegations: Option>, ) -> Result { ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); let free_balance = T::Currency::free_balance(who); @@ -1952,13 +1987,6 @@ impl Pallet { .all(|(a, b)| lowest_stake <= a.stake && a == b) }); ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); - if let Some(pool_item) = lowest_item { - if let Some(mut lowest_juror_info) = >::get(&pool_item.juror) { - let now = >::block_number(); - lowest_juror_info.prepare_exit_at = Some(now); - >::insert(&pool_item.juror, lowest_juror_info); - } - } // remove the lowest staked juror jurors.remove(0); } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 5f7cc6cfb..669710189 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -35,8 +35,8 @@ use zeitgeist_primitives::{ constants::mock::{ AppealBond, BlockHashCount, BlocksPerYear, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, InflationPeriod, MaxAppeals, MaxApprovals, - MaxDelegations, MaxDraws, MaxJurors, MaxReserves, MinJurorStake, MinimumPeriod, PmPalletId, - RequestInterval, BASE, + MaxDelegations, MaxJurors, MaxReserves, MaxSelectedDraws, MinJurorStake, MinimumPeriod, + PmPalletId, RequestInterval, BASE, }, traits::DisputeResolutionApi, types::{ @@ -151,7 +151,7 @@ impl crate::Config for Runtime { type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDelegations = MaxDelegations; - type MaxDraws = MaxDraws; + type MaxSelectedDraws = MaxSelectedDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index dc314cbb1..ee8aaadd2 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -27,8 +27,8 @@ use crate::{ }, mock_storage::pallet::MarketIdsPerDisputeBlock, types::{CourtStatus, Draw, Vote}, - AppealInfo, BalanceOf, Courts, Draws, Error, Event, JurorInfo, JurorInfoOf, JurorPool, - JurorPoolItem, JurorPoolOf, Jurors, MarketIdOf, MarketOf, NegativeImbalanceOf, RequestBlock, + AppealInfo, BalanceOf, Courts, Error, Event, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, + JurorPoolOf, Jurors, MarketIdOf, MarketOf, NegativeImbalanceOf, RequestBlock, SelectedDraws, }; use alloc::collections::BTreeMap; use frame_support::{ @@ -129,11 +129,11 @@ fn fill_appeals(market_id: &MarketIdOf, appeal_number: usize) { fn put_alice_in_draw(market_id: MarketIdOf, stake: BalanceOf) { // trick a little bit to let alice be part of the ("random") selection - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); assert!(!draws.is_empty()); let slashable = MinJurorStake::get(); draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; - >::insert(market_id, draws); + >::insert(market_id, draws); >::insert( ALICE, JurorInfo { @@ -500,7 +500,7 @@ fn prepare_exit_court_fails_juror_already_prepared_to_exit() { assert_noop!( Court::prepare_exit_court(Origin::signed(ALICE)), - Error::::JurorAlreadyPreparedToExit + Error::::JurorAlreadyPreparedExit ); }); } @@ -614,7 +614,7 @@ fn exit_court_fails_juror_not_prepared_to_exit() { assert_noop!( Court::exit_court(Origin::signed(ALICE), ALICE), - Error::::JurorNotPreparedToExit + Error::::PrepareExitAtNotPresent ); }); } @@ -646,12 +646,12 @@ fn vote_works() { assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); // trick a little bit to let alice be part of the ("random") selection - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); assert_eq!(draws.len(), 5usize); let slashable = MinJurorStake::get(); let alice_index = 3usize; draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; - >::insert(market_id, draws); + >::insert(market_id, draws); >::insert( ALICE, JurorInfo { @@ -662,7 +662,7 @@ fn vote_works() { }, ); - let old_draws = >::get(market_id); + let old_draws = >::get(market_id); run_to_block(>::get() + 1); @@ -672,7 +672,7 @@ fn vote_works() { assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); System::assert_last_event(Event::JurorVoted { juror: ALICE, market_id, commitment }.into()); - let new_draws = >::get(market_id); + let new_draws = >::get(market_id); for (i, (old_draw, new_draw)) in old_draws.iter().zip(new_draws.iter()).enumerate() { if i == alice_index { continue; @@ -706,7 +706,7 @@ fn vote_overwrite_works() { let wrong_commitment = BlakeTwo256::hash_of(&(ALICE, wrong_outcome, salt)); assert_ok!(Court::vote(Origin::signed(ALICE), market_id, wrong_commitment)); assert_eq!( - >::get(market_id)[0].vote, + >::get(market_id)[0].vote, Vote::Secret { commitment: wrong_commitment } ); @@ -717,7 +717,7 @@ fn vote_overwrite_works() { assert_ok!(Court::vote(Origin::signed(ALICE), market_id, new_commitment)); assert_ne!(wrong_commitment, new_commitment); assert_eq!( - >::get(market_id)[0].vote, + >::get(market_id)[0].vote, Vote::Secret { commitment: new_commitment } ); }); @@ -759,10 +759,10 @@ fn vote_fails_if_vote_state_incorrect( let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); assert!(!draws.is_empty()); draws[0] = Draw { juror: ALICE, weight: 101, vote, slashable: 42u128 }; - >::insert(market_id, draws); + >::insert(market_id, draws); run_to_block(>::get() + 1); @@ -782,9 +782,9 @@ fn vote_fails_if_caller_not_in_draws() { fill_juror_pool(); let market_id = initialize_court(); - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); draws.retain(|draw| draw.juror != ALICE); - >::insert(market_id, draws); + >::insert(market_id, draws); run_to_block(>::get() + 1); @@ -793,7 +793,7 @@ fn vote_fails_if_caller_not_in_draws() { let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); assert_noop!( Court::vote(Origin::signed(ALICE), market_id, commitment), - Error::::CallerNotInDraws + Error::::CallerNotInSelectedDraws ); }); } @@ -831,12 +831,12 @@ fn reveal_vote_works() { assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); // trick a little bit to let alice be part of the ("random") selection - let mut draws = >::get(market_id); + let mut draws = >::get(market_id); assert_eq!(draws.len(), 5usize); let slashable = MinJurorStake::get(); let alice_index = 3usize; draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; - >::insert(market_id, draws); + >::insert(market_id, draws); >::insert( ALICE, JurorInfo { @@ -854,7 +854,7 @@ fn reveal_vote_works() { let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); - let old_draws = >::get(market_id); + let old_draws = >::get(market_id); run_blocks(CourtVotePeriod::get() + 1); @@ -864,7 +864,7 @@ fn reveal_vote_works() { .into(), ); - let new_draws = >::get(market_id); + let new_draws = >::get(market_id); for (i, (old_draw, new_draw)) in old_draws.iter().zip(new_draws.iter()).enumerate() { if i == alice_index { continue; @@ -935,7 +935,7 @@ fn reveal_vote_fails_if_juror_not_drawn() { run_blocks(CourtVotePeriod::get() + 1); - >::mutate(market_id, |draws| { + >::mutate(market_id, |draws| { draws.retain(|draw| draw.juror != ALICE); }); @@ -987,7 +987,7 @@ fn reveal_vote_fails_if_juror_not_voted() { run_blocks(CourtVotePeriod::get() + 1); - >::mutate(market_id, |draws| { + >::mutate(market_id, |draws| { draws.iter_mut().for_each(|draw| { if draw.juror == ALICE { draw.vote = Vote::Drawn; @@ -1048,7 +1048,7 @@ fn denounce_vote_works() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, commitment, salt) = set_alice_after_vote(outcome.clone()); - let old_draws = >::get(market_id); + let old_draws = >::get(market_id); assert!( old_draws .iter() @@ -1076,7 +1076,7 @@ fn denounce_vote_works() { .into(), ); - let new_draws = >::get(market_id); + let new_draws = >::get(market_id); assert_eq!(old_draws[1..], new_draws[1..]); assert_eq!(old_draws[0].juror, ALICE); assert_eq!(old_draws[0].juror, new_draws[0].juror); @@ -1161,7 +1161,7 @@ fn denounce_vote_fails_if_juror_not_drawn() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); - >::mutate(market_id, |draws| { + >::mutate(market_id, |draws| { draws.retain(|draw| draw.juror != ALICE); }); @@ -1192,7 +1192,7 @@ fn denounce_vote_fails_if_juror_not_voted() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); - >::mutate(market_id, |draws| { + >::mutate(market_id, |draws| { draws.iter_mut().for_each(|draw| { if draw.juror == ALICE { draw.vote = Vote::Drawn; @@ -1342,14 +1342,14 @@ fn appeal_overrides_last_draws() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); - let last_draws = >::get(market_id); + let last_draws = >::get(market_id); assert!(!last_draws.len().is_zero()); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); - let draws = >::get(market_id); + let draws = >::get(market_id); assert_ne!(draws, last_draws); }); } @@ -1360,7 +1360,7 @@ fn appeal_draws_total_weight_is_correct() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); - let last_draws = >::get(market_id); + let last_draws = >::get(market_id); let last_draws_total_weight = last_draws.iter().map(|draw| draw.weight).sum::(); assert_eq!(last_draws_total_weight, Court::necessary_jurors_weight(0usize) as u32); @@ -1369,7 +1369,7 @@ fn appeal_draws_total_weight_is_correct() { assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); let neccessary_juror_weight = Court::necessary_jurors_weight(1usize) as u32; - let draws = >::get(market_id); + let draws = >::get(market_id); let draws_total_weight = draws.iter().map(|draw| draw.weight).sum::(); assert_eq!(draws_total_weight, neccessary_juror_weight); }); @@ -1556,7 +1556,7 @@ fn appeal_adds_last_appeal() { fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); - let last_draws = >::get(market_id); + let last_draws = >::get(market_id); let appealed_outcome = Court::get_latest_resolved_outcome(&market_id, last_draws.as_slice()).unwrap(); @@ -1587,7 +1587,7 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { let salt = ::Hash::default(); let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); - let draws: crate::DrawsOf = vec![ + let draws: crate::SelectedDrawsOf = vec![ Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() }, Draw { juror: BOB, @@ -1612,7 +1612,7 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { .try_into() .unwrap(); let old_draws = draws.clone(); - >::insert(market_id, draws); + >::insert(market_id, draws); run_to_block(>::get() + 1); @@ -1734,12 +1734,12 @@ fn reassign_juror_stakes_removes_draws() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().unwrap(); - let draws = >::get(market_id); + let draws = >::get(market_id); assert!(!draws.is_empty()); assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); - let draws = >::get(market_id); + let draws = >::get(market_id); assert!(draws.is_empty()); }); } @@ -1796,7 +1796,7 @@ fn reassign_juror_stakes_decreases_active_lock() { } }); - let draws: crate::DrawsOf = vec![ + let draws: crate::SelectedDrawsOf = vec![ Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: alice_slashable }, Draw { juror: BOB, @@ -1819,7 +1819,7 @@ fn reassign_juror_stakes_decreases_active_lock() { ] .try_into() .unwrap(); - >::insert(market_id, draws); + >::insert(market_id, draws); run_to_block(>::get() + 1); @@ -1857,7 +1857,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let wrong_outcome_0 = OutcomeReport::Scalar(69u128); let wrong_outcome_1 = OutcomeReport::Scalar(56u128); - let draws: crate::DrawsOf = vec![ + let draws: crate::SelectedDrawsOf = vec![ Draw { juror: ALICE, weight: 1, @@ -1886,7 +1886,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { .try_into() .unwrap(); let last_draws = draws.clone(); - >::insert(market_id, draws); + >::insert(market_id, draws); run_to_block(>::get() + 1); @@ -1947,7 +1947,7 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { let wrong_outcome_0 = OutcomeReport::Scalar(69u128); let wrong_outcome_1 = OutcomeReport::Scalar(56u128); - let draws: crate::DrawsOf = vec![ + let draws: crate::SelectedDrawsOf = vec![ Draw { juror: ALICE, weight: 1, @@ -1976,7 +1976,7 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { .try_into() .unwrap(); let last_draws = draws.clone(); - >::insert(market_id, draws); + >::insert(market_id, draws); run_to_block(>::get() + 1); @@ -2179,9 +2179,9 @@ fn on_global_dispute_removes_draws() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); let market = MarketCommons::market(&market_id).unwrap(); - assert!(>::contains_key(market_id)); + assert!(>::contains_key(market_id)); assert_ok!(Court::on_global_dispute(&market_id, &market)); - assert!(!>::contains_key(market_id)); + assert!(!>::contains_key(market_id)); }); } @@ -2258,9 +2258,7 @@ fn choose_multiple_weighted_works() { let _ = Balances::deposit(&juror, amount).unwrap(); assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - let mut jurors = JurorPool::::get(); - let random_jurors = - Court::choose_multiple_weighted(&mut jurors, necessary_jurors_weight).unwrap(); + let random_jurors = Court::choose_multiple_weighted(necessary_jurors_weight).unwrap(); assert_eq!( random_jurors.iter().map(|draw| draw.weight).sum::() as usize, necessary_jurors_weight @@ -2314,7 +2312,7 @@ fn appeal_reduces_active_lock_from_old_draws() { let outcome = OutcomeReport::Scalar(42u128); let (market_id, _, _) = set_alice_after_vote(outcome); - let old_draws = >::get(market_id); + let old_draws = >::get(market_id); assert!(!old_draws.is_empty()); old_draws.iter().for_each(|draw| { let juror = draw.juror; @@ -2327,7 +2325,7 @@ fn appeal_reduces_active_lock_from_old_draws() { assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); - let new_draws = >::get(market_id); + let new_draws = >::get(market_id); old_draws.iter().for_each(|draw| { let juror = draw.juror; let juror_info = >::get(juror).unwrap(); @@ -2369,7 +2367,7 @@ fn on_dispute_fails_if_court_already_exists() { fn on_dispute_inserts_draws() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); - let draws = >::get(market_id); + let draws = >::get(market_id); assert_eq!( draws[0], Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } @@ -2467,7 +2465,7 @@ fn check_appeal_bond() { } fn prepare_draws(market_id: &MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { - let mut draws: crate::DrawsOf = vec![].try_into().unwrap(); + let mut draws: crate::SelectedDrawsOf = vec![].try_into().unwrap(); for (i, (outcome_index, weight)) in outcomes_with_weights.iter().enumerate() { // offset to not conflict with other jurors let offset_i = (i + 1000) as u128; @@ -2484,7 +2482,7 @@ fn prepare_draws(market_id: &MarketIdOf, outcomes_with_weights: Vec<(u1 }) .unwrap(); } - >::insert(market_id, draws); + >::insert(market_id, draws); } #[test] @@ -2495,14 +2493,14 @@ fn get_winner_works() { vec![(1000u128, 8), (1001u128, 5), (1002u128, 42), (1003u128, 13)]; prepare_draws(&market_id, outcomes_and_weights); - let draws = >::get(market_id); + let draws = >::get(market_id); let winner = Court::get_winner(draws.as_slice(), None).unwrap(); assert_eq!(winner, OutcomeReport::Scalar(1002u128)); let outcomes_and_weights = vec![(1000u128, 2), (1000u128, 4), (1001u128, 4), (1001u128, 3)]; prepare_draws(&market_id, outcomes_and_weights); - let draws = >::get(market_id); + let draws = >::get(market_id); let winner = Court::get_winner(draws.as_slice(), None).unwrap(); assert_eq!(winner, OutcomeReport::Scalar(1001u128)); }); @@ -2512,7 +2510,7 @@ fn get_winner_works() { fn get_winner_returns_none_for_no_revealed_draws() { ExtBuilder::default().build().execute_with(|| { let market_id = initialize_court(); - let draws = >::get(market_id); + let draws = >::get(market_id); let winner = Court::get_winner(draws.as_slice(), None); assert_eq!(winner, None); }); @@ -2537,7 +2535,7 @@ fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { .unwrap(); >::insert(market_id, court); - let draws = >::get(market_id); + let draws = >::get(market_id); let latest = Court::get_latest_resolved_outcome(&market_id, draws.as_slice()).unwrap(); assert_eq!(latest, appealed_outcome); assert!(latest != ORACLE_REPORT); @@ -2550,7 +2548,7 @@ fn get_latest_resolved_outcome_selects_oracle_report() { let market_id = initialize_court(); let market = MarketCommons::market(&market_id).unwrap(); assert_eq!(market.report.unwrap().outcome, ORACLE_REPORT); - let draws = >::get(market_id); + let draws = >::get(market_id); assert_eq!( Court::get_latest_resolved_outcome(&market_id, draws.as_slice()).unwrap(), ORACLE_REPORT @@ -2576,13 +2574,14 @@ fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { ); jurors.try_push(pool_item.clone()).unwrap(); } + >::put(jurors); let nonce_0 = 42u64; >::put(nonce_0); // randomness is mocked and purely based on the nonce // thus a different nonce will result in a different seed (disregarding hash collisions) let first_random_seed = Court::get_random_seed(nonce_0); - let first_random_list = Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); + let first_random_list = Court::choose_multiple_weighted(3).unwrap(); run_blocks(1); @@ -2591,7 +2590,7 @@ fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { let second_random_seed = Court::get_random_seed(nonce_1); assert_ne!(first_random_seed, second_random_seed); - let second_random_list = Court::choose_multiple_weighted(&mut jurors, 3).unwrap(); + let second_random_list = Court::choose_multiple_weighted(3).unwrap(); // the two lists contain different jurors for juror in &first_random_list { @@ -2637,8 +2636,9 @@ fn random_jurors_returns_a_subset_of_jurors() { ); jurors.try_push(pool_item.clone()).unwrap(); } + >::put(jurors); - let random_jurors = Court::choose_multiple_weighted(&mut jurors, 2).unwrap(); + let random_jurors = Court::choose_multiple_weighted(2).unwrap(); for draw in random_jurors { assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.juror == draw.juror)); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index f2aea192f..059eb0e52 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . extern crate alloc; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use zeitgeist_primitives::types::OutcomeReport; /// The general information about a particular juror. #[derive( @@ -36,7 +36,7 @@ pub struct JurorInfo { pub active_lock: Balance, /// The block number when a juror exit from court was requested. pub prepare_exit_at: Option, - pub delegations: Delegations, + pub delegations: Option, } /// The raw information behind the secret hash of a juror's vote. @@ -289,3 +289,13 @@ pub struct JurorVoteWithStakes { // the value is the delegated stake pub delegations: Vec<(AccountId, Balance)>, } + +impl Default for JurorVoteWithStakes { + fn default() -> Self { + JurorVoteWithStakes { self_info: None, delegations: vec![] } + } +} + +pub enum SelectionError { + NoValidDelegatedJuror, +} diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 5ea2ca7f2..cabc397ec 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -47,12 +47,12 @@ use frame_support::{traits::Get, weights::Weight}; pub trait WeightInfoZeitgeist { fn join_court(j: u32) -> Weight; fn prepare_exit_court(j: u32) -> Weight; - fn exit_court_remove(j: u32) -> Weight; - fn exit_court_set(j: u32) -> Weight; + fn exit_court_remove() -> Weight; + fn exit_court_set() -> Weight; fn vote(d: u32) -> Weight; fn denounce_vote(d: u32) -> Weight; fn reveal_vote(d: u32) -> Weight; - fn appeal(d: u32, a: u32, r: u32) -> Weight; + fn appeal(d: u32, a: u32, r: u32, f: u32) -> Weight; fn reassign_juror_stakes(d: u32) -> Weight; fn set_inflation() -> Weight; fn handle_inflation(j: u32) -> Weight; @@ -81,23 +81,17 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court Jurors (r:1 w:1) - // Storage: Court JurorPool (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - fn exit_court_remove(j: u32) -> Weight { - Weight::from_ref_time(38_554_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(28_000).saturating_mul(j.into())) - .saturating_add(T::DbWeight::get().reads(3)) + fn exit_court_remove() -> Weight { + Weight::from_ref_time(39_000_000) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Jurors (r:1 w:1) - // Storage: Court JurorPool (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - fn exit_court_set(j: u32) -> Weight { - Weight::from_ref_time(37_829_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(28_000).saturating_mul(j.into())) - .saturating_add(T::DbWeight::get().reads(3)) + fn exit_court_set() -> Weight { + Weight::from_ref_time(37_000_000) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Courts (r:1 w:0) @@ -132,20 +126,22 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court Courts (r:1 w:1) // Storage: MarketCommons Markets (r:1 w:0) - // Storage: Court Draws (r:1 w:1) + // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) // Storage: Court Jurors (r:41 w:41) - // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Court RequestBlock (r:1 w:0) + // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Balances Reserves (r:1 w:1) - fn appeal(j: u32, a: u32, _r: u32) -> Weight { + fn appeal(j: u32, a: u32, r: u32, _f: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(459_000).saturating_mul(j.into())) - // Standard Error: 374_000 - .saturating_add(Weight::from_ref_time(258_163_000).saturating_mul(a.into())) + // Standard Error: 2_000 + .saturating_add(Weight::from_ref_time(661_000).saturating_mul(j.into())) + // Standard Error: 905_000 + .saturating_add(Weight::from_ref_time(332_344_000).saturating_mul(a.into())) + // Standard Error: 36_000 + .saturating_add(Weight::from_ref_time(1_522_000).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().reads((28_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(8)) diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 9a99f5cbe..ddd3372b9 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -42,13 +42,13 @@ use zeitgeist_primitives::{ CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, InflationPeriod, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, - MaxCategories, MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxDraws, MaxEditReasonLen, + MaxCategories, MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, - MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, - MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, MinLiquidity, - MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, - OutcomeFactor, OutsiderBond, PmPalletId, RequestInterval, SimpleDisputesPalletId, - SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, + MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxSubsidyPeriod, MaxSwapFee, + MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, + MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, + OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, RequestInterval, + SimpleDisputesPalletId, SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -261,7 +261,7 @@ impl zrml_court::Config for Runtime { type MarketCommons = MarketCommons; type MaxAppeals = MaxAppeals; type MaxDelegations = MaxDelegations; - type MaxDraws = MaxDraws; + type MaxSelectedDraws = MaxSelectedDraws; type MaxJurors = MaxJurors; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 2a3e10c30..e8d3da7cf 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -40,8 +40,8 @@ use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_runtime::traits::{AccountIdConversion, Hash, SaturatedConversion, Zero}; use zeitgeist_primitives::{ constants::mock::{ - MaxAppeals, MaxDraws, MinJurorStake, OutcomeBond, OutcomeFactor, OutsiderBond, BASE, CENT, - MILLISECS_PER_BLOCK, + MaxAppeals, MaxSelectedDraws, MinJurorStake, OutcomeBond, OutcomeFactor, OutsiderBond, + BASE, CENT, MILLISECS_PER_BLOCK, }, traits::Swaps as SwapsPalletApi, types::{ @@ -3002,8 +3002,8 @@ fn it_resolves_a_disputed_court_market() { run_to_block(vote_start); // overwrite draws to disregard randomness - zrml_court::Draws::::remove(market_id); - let mut draws = zrml_court::Draws::::get(market_id); + zrml_court::SelectedDraws::::remove(market_id); + let mut draws = zrml_court::SelectedDraws::::get(market_id); for juror in &[juror_0, juror_1, juror_2, juror_3, juror_4, juror_5] { draws .try_push(Draw { @@ -3015,7 +3015,7 @@ fn it_resolves_a_disputed_court_market() { .unwrap(); } let old_draws = draws.clone(); - zrml_court::Draws::::insert(market_id, draws); + zrml_court::SelectedDraws::::insert(market_id, draws); let salt = ::Hash::default(); @@ -3133,7 +3133,7 @@ fn simulate_appeal_cycle(market_id: MarketId) { let wrong_outcome = OutcomeReport::Categorical(1); - let draws = zrml_court::Draws::::get(market_id); + let draws = zrml_court::SelectedDraws::::get(market_id); for draw in &draws { let commitment = BlakeTwo256::hash_of(&(draw.juror, wrong_outcome.clone(), salt)); assert_ok!(Court::vote(Origin::signed(draw.juror), market_id, commitment)); @@ -3165,7 +3165,7 @@ fn simulate_appeal_cycle(market_id: MarketId) { fn it_appeals_a_court_market_to_global_dispute() { let test = |base_asset: Asset| { let mut free_before = BTreeMap::new(); - let jurors = 1000..(1000 + MaxDraws::get() as u128); + let jurors = 1000..(1000 + MaxSelectedDraws::get() as u128); for j in jurors { let amount = MinJurorStake::get() + j as u128; assert_ok!(AssetManager::deposit(Asset::Ztg, &j, amount + SENTINEL_AMOUNT)); From afd9d618a0eaaf5643733db0d092f68f809123e7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 17 Apr 2023 08:23:00 +0200 Subject: [PATCH 253/368] add correct mdm benchmarking on_dispute --- primitives/src/traits/dispute_api.rs | 3 ++ zrml/authorized/src/benchmarks.rs | 14 +++++--- zrml/authorized/src/lib.rs | 7 +++- zrml/authorized/src/weights.rs | 4 +++ zrml/court/src/benchmarks.rs | 15 ++++++--- zrml/court/src/lib.rs | 38 +++++++++++++++++++++- zrml/court/src/weights.rs | 5 +++ zrml/prediction-markets/src/lib.rs | 44 +++++++++++++++++++------- zrml/simple-disputes/src/benchmarks.rs | 9 +++++- zrml/simple-disputes/src/lib.rs | 6 +++- zrml/simple-disputes/src/weights.rs | 5 +++ 11 files changed, 126 insertions(+), 24 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 2a137f243..a47f123c5 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -55,6 +55,9 @@ pub trait DisputeApi { /// associated pallet. **May** assume that `market.dispute_mechanism` refers to the calling dispute API. fn on_dispute(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; + /// Return the weight of the `on_dispute` function. + fn on_dispute_weight() -> Weight; + /// Manage market resolution of a disputed market. /// /// **Should** only be called if the market was disputed before resolving. **May** assume that diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index d4f349161..cbab9b329 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -22,9 +22,7 @@ )] #![cfg(feature = "runtime-benchmarks")] -#[cfg(test)] -use crate::Pallet as Authorized; -use crate::{market_mock, AuthorizedOutcomeReports, Call, Config, Pallet}; +use crate::{market_mock, AuthorizedOutcomeReports, Call, Config, Pallet as Authorized, Pallet}; use frame_benchmarking::benchmarks; use frame_support::{ dispatch::UnfilteredDispatchable, @@ -32,7 +30,7 @@ use frame_support::{ }; use sp_runtime::traits::Saturating; use zeitgeist_primitives::{ - traits::DisputeResolutionApi, + traits::{DisputeApi, DisputeResolutionApi}, types::{AuthorityReport, OutcomeReport}, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -96,6 +94,14 @@ benchmarks! { assert_eq!(AuthorizedOutcomeReports::::get(market_id).unwrap(), report); } + on_dispute_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + }: { + Authorized::::on_dispute(&market_id, &market).unwrap(); + } + impl_benchmark_test_suite!( Authorized, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 142a5cb1b..33eb14f71 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -40,7 +40,7 @@ mod pallet { use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo}, ensure, - pallet_prelude::{ConstU32, EnsureOrigin, OptionQuery, StorageMap}, + pallet_prelude::{ConstU32, EnsureOrigin, OptionQuery, StorageMap, Weight}, traits::{Currency, Get, Hooks, IsType, StorageVersion}, PalletId, Twox64Concat, }; @@ -216,6 +216,11 @@ mod pallet { Ok(()) } + fn on_dispute_weight() -> Weight { + // TODO T::WeightInfo::on_dispute_weight() + Weight::zero() + } + fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, diff --git a/zrml/authorized/src/weights.rs b/zrml/authorized/src/weights.rs index 685066985..b27ee7161 100644 --- a/zrml/authorized/src/weights.rs +++ b/zrml/authorized/src/weights.rs @@ -48,6 +48,7 @@ use frame_support::{traits::Get, weights::Weight}; pub trait WeightInfoZeitgeist { fn authorize_market_outcome_first_report(m: u32) -> Weight; fn authorize_market_outcome_existing_report() -> Weight; + fn on_dispute_weight() -> Weight; } /// Weight functions for zrml_authorized (automatically generated) @@ -70,4 +71,7 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + fn on_dispute_weight() -> Weight { + Weight::from_ref_time(0) + } } diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index d78f614d1..994ac8c2e 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -22,14 +22,13 @@ )] #![cfg(feature = "runtime-benchmarks")] -#[cfg(test)] -use crate::Pallet as Court; -use crate::{BalanceOf, Call, Config, CurrencyOf, Pallet}; +use crate::{market_mock, BalanceOf, Call, Config, CurrencyOf, Pallet as Court, Pallet}; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_support::{dispatch::UnfilteredDispatchable, traits::Currency}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; -use zeitgeist_primitives::types::OutcomeReport; +use zeitgeist_primitives::{traits::DisputeApi, types::OutcomeReport}; +use zrml_market_commons::MarketCommonsPalletApi; fn deposit(caller: &T::AccountId) where @@ -66,6 +65,14 @@ benchmarks! { deposit_and_join_court::(&caller); }: _(RawOrigin::Signed(caller), market_id, outcome) + on_dispute_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + }: { + Court::::on_dispute(&market_id, &market).unwrap(); + } + impl_benchmark_test_suite!( Court, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index e16c4f1fe..920a5b38a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -52,7 +52,7 @@ mod pallet { use frame_support::{ dispatch::DispatchResult, ensure, - pallet_prelude::{CountedStorageMap, StorageDoubleMap, StorageValue, ValueQuery}, + pallet_prelude::{CountedStorageMap, StorageDoubleMap, StorageValue, ValueQuery, Weight}, traits::{ BalanceStatus, Currency, Get, Hooks, IsType, NamedReservableCurrency, Randomness, StorageVersion, @@ -561,6 +561,10 @@ mod pallet { Ok(()) } + fn on_dispute_weight() -> Weight { + Weight::zero() + } + // Set jurors that sided on the second most voted outcome as tardy. Jurors are only // rewarded if sided on the most voted outcome but jurors that voted second most // voted outcome (winner of the losing majority) are placed as tardy instead of @@ -682,3 +686,35 @@ mod pallet { (T::BlockNumber, OutcomeReport), >; } + +#[cfg(any(feature = "runtime-benchmarks", test))] +pub(crate) fn market_mock() -> MarketOf +where + T: crate::Config, +{ + use frame_support::traits::Get; + use sp_runtime::traits::AccountIdConversion; + use zeitgeist_primitives::types::{Asset, MarketBonds, ScoringRule}; + + zeitgeist_primitives::types::Market { + base_asset: Asset::Ztg, + creation: zeitgeist_primitives::types::MarketCreation::Permissionless, + creator_fee: 0, + creator: T::PalletId::get().into_account_truncating(), + market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=100), + dispute_mechanism: zeitgeist_primitives::types::MarketDisputeMechanism::Court, + metadata: Default::default(), + oracle: T::PalletId::get().into_account_truncating(), + period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), + deadlines: zeitgeist_primitives::types::Deadlines { + grace_period: 1_u32.into(), + oracle_duration: 1_u32.into(), + dispute_duration: 1_u32.into(), + }, + report: None, + resolved_outcome: None, + scoring_rule: ScoringRule::CPMM, + status: zeitgeist_primitives::types::MarketStatus::Disputed, + bonds: MarketBonds::default(), + } +} diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 96b5e68a9..eed9d2298 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -49,6 +49,7 @@ pub trait WeightInfoZeitgeist { fn exit_court() -> Weight; fn join_court() -> Weight; fn vote() -> Weight; + fn on_dispute_weight() -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -79,4 +80,8 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + + fn on_dispute_weight() -> Weight { + Weight::from_ref_time(0) + } } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 4948976fe..e07a54a90 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -64,13 +64,16 @@ mod pallet { constants::MILLISECS_PER_BLOCK, traits::{DisputeApi, DisputeResolutionApi, Swaps, ZeitgeistAssetManager}, types::{ - Asset, Bond, Deadlines, GlobalDisputeItem, Market, MarketBonds, MarketCreation, - MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, MultiHash, - OldMarketDispute, OutcomeReport, Report, ScalarPosition, ScoringRule, SubsidyUntil, + Asset, Bond, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, MultiHash, OldMarketDispute, OutcomeReport, + Report, ScalarPosition, ScoringRule, SubsidyUntil, }, }; #[cfg(feature = "with-global-disputes")] - use zrml_global_disputes::GlobalDisputesPalletApi; + use { + zeitgeist_primitives::types::GlobalDisputeItem, + zrml_global_disputes::GlobalDisputesPalletApi, + }; use zrml_liquidity_mining::LiquidityMiningPalletApi; use zrml_market_commons::MarketCommonsPalletApi; @@ -613,7 +616,13 @@ mod pallet { /// # Weight /// /// Complexity: `O(n)`, where `n` is the number of outstanding disputes. - #[pallet::weight(T::WeightInfo::dispute_authorized())] + #[pallet::weight( + T::WeightInfo::dispute_authorized().saturating_add( + T::Court::on_dispute_weight().saturating_add( + T::SimpleDisputes::on_dispute_weight() + ) + ) + )] #[transactional] pub fn dispute( origin: OriginFor, @@ -624,16 +633,22 @@ mod pallet { let market = >::market(&market_id)?; ensure!(market.status == MarketStatus::Reported, Error::::InvalidMarketStatus); - // TODO(#782): use multiple benchmarks paths for different dispute mechanisms - match market.dispute_mechanism { + let overweight = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::on_dispute(&market_id, &market)? + T::Authorized::on_dispute(&market_id, &market)?; + T::Court::on_dispute_weight() + .saturating_add(T::SimpleDisputes::on_dispute_weight()) + } + MarketDisputeMechanism::Court => { + T::Court::on_dispute(&market_id, &market)?; + T::Authorized::on_dispute_weight() + .saturating_add(T::SimpleDisputes::on_dispute_weight()) } - MarketDisputeMechanism::Court => T::Court::on_dispute(&market_id, &market)?, MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_dispute(&market_id, &market)? + T::SimpleDisputes::on_dispute(&market_id, &market)?; + T::Court::on_dispute_weight().saturating_add(T::Authorized::on_dispute_weight()) } - } + }; let dispute_bond = T::DisputeBond::get(); @@ -647,7 +662,12 @@ mod pallet { Self::deposit_event(Event::MarketDisputed(market_id, MarketStatus::Disputed)); - Ok((Some(T::WeightInfo::dispute_authorized())).into()) + let full_weight = T::WeightInfo::dispute_authorized().saturating_add( + T::Court::on_dispute_weight() + .saturating_add(T::SimpleDisputes::on_dispute_weight()), + ); + + Ok((Some(full_weight.saturating_sub(overweight))).into()) } /// Create a permissionless market, buy complete sets and deploy a pool with specified diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index 1d2a9d5e3..6fd425a0f 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -22,7 +22,6 @@ #![allow(clippy::type_complexity)] #![cfg(feature = "runtime-benchmarks")] -#[cfg(test)] use crate::Pallet as SimpleDisputes; use super::*; @@ -83,6 +82,14 @@ benchmarks! { T::AssetManager::deposit(Asset::Ztg, &caller, bond).unwrap(); }: _(RawOrigin::Signed(caller.clone()), market_id, outcome) + on_dispute_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + }: { + SimpleDisputes::::on_dispute(&market_id, &market).unwrap(); + } + impl_benchmark_test_suite!( SimpleDisputes, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index c649177aa..15f9e6427 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -48,7 +48,7 @@ mod pallet { dispatch::DispatchResult, ensure, pallet_prelude::{ - Blake2_128Concat, ConstU32, DispatchResultWithPostInfo, StorageMap, ValueQuery, + Blake2_128Concat, ConstU32, DispatchResultWithPostInfo, StorageMap, ValueQuery, Weight, }, traits::{Currency, Get, Hooks, Imbalance, IsType, NamedReservableCurrency}, transactional, BoundedVec, PalletId, @@ -295,6 +295,10 @@ mod pallet { Ok(()) } + fn on_dispute_weight() -> Weight { + Weight::zero() + } + fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, diff --git a/zrml/simple-disputes/src/weights.rs b/zrml/simple-disputes/src/weights.rs index 8d2851d61..2143935cc 100644 --- a/zrml/simple-disputes/src/weights.rs +++ b/zrml/simple-disputes/src/weights.rs @@ -46,6 +46,7 @@ use frame_support::{traits::Get, weights::Weight}; /// zrml_simple_disputes (automatically generated) pub trait WeightInfoZeitgeist { fn suggest_outcome(d: u32, r: u32, e: u32) -> Weight; + fn on_dispute_weight() -> Weight; } /// Weight functions for zrml_simple_disputes (automatically generated) @@ -66,4 +67,8 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } + + fn on_dispute_weight() -> Weight { + Weight::from_ref_time(0) + } } From af0a25a94b6c744df7547e5fd1ed6c3c97ae108a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 17 Apr 2023 08:25:34 +0200 Subject: [PATCH 254/368] use on_dispute_weight inside api --- zrml/authorized/src/lib.rs | 3 +-- zrml/court/src/lib.rs | 2 +- zrml/simple-disputes/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 33eb14f71..d98cba895 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -217,8 +217,7 @@ mod pallet { } fn on_dispute_weight() -> Weight { - // TODO T::WeightInfo::on_dispute_weight() - Weight::zero() + T::WeightInfo::on_dispute_weight() } fn on_resolution( diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 920a5b38a..77ea0d9f3 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -562,7 +562,7 @@ mod pallet { } fn on_dispute_weight() -> Weight { - Weight::zero() + T::WeightInfo::on_dispute_weight() } // Set jurors that sided on the second most voted outcome as tardy. Jurors are only diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 15f9e6427..303d11d2b 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -296,7 +296,7 @@ mod pallet { } fn on_dispute_weight() -> Weight { - Weight::zero() + T::WeightInfo::on_dispute_weight() } fn on_resolution( From b88aca43c8c4954d7fecae5aa61084e8f67a47fc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 17 Apr 2023 12:53:32 +0200 Subject: [PATCH 255/368] improve mdm weight technique --- primitives/src/market.rs | 12 +++ primitives/src/traits/dispute_api.rs | 31 ++++++- zrml/authorized/src/benchmarks.rs | 78 +++++++++++++++- zrml/authorized/src/lib.rs | 36 +++++++- zrml/authorized/src/weights.rs | 24 +++++ zrml/court/src/lib.rs | 38 +++++++- zrml/court/src/weights.rs | 25 +++++- zrml/prediction-markets/src/lib.rs | 42 ++++++--- zrml/simple-disputes/src/benchmarks.rs | 120 ++++++++++++++++++++----- zrml/simple-disputes/src/lib.rs | 74 ++++++++++++++- zrml/simple-disputes/src/weights.rs | 58 ++++++++++++ 11 files changed, 492 insertions(+), 46 deletions(-) diff --git a/primitives/src/market.rs b/primitives/src/market.rs index 6eeb4e9ca..a7a6900d2 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -204,6 +204,18 @@ pub struct MarketDispute { pub bond: Balance, } +#[derive(Default)] +pub enum MDMWeight { + Authorized, + Court, + SimpleDisputes { + market_id: MarketId, + market: Market, + }, + #[default] + Default, +} + /// How a market should resolve disputes #[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub enum MarketDisputeMechanism { diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index a47f123c5..f67281e53 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -20,7 +20,7 @@ extern crate alloc; use crate::{ outcome_report::OutcomeReport, - types::{Asset, GlobalDisputeItem, Market}, + types::{Asset, GlobalDisputeItem, MDMWeight, Market}, }; use alloc::vec::Vec; use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight}; @@ -37,6 +37,15 @@ type MarketOfDisputeApi = Market< Asset<::MarketId>, >; +type MDMWeightOf = MDMWeight< + ::MarketId, + ::AccountId, + ::Balance, + ::BlockNumber, + ::Moment, + Asset<::MarketId>, +>; + type GlobalDisputeItemOfDisputeApi = GlobalDisputeItem<::AccountId, ::Balance>; @@ -56,7 +65,7 @@ pub trait DisputeApi { fn on_dispute(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; /// Return the weight of the `on_dispute` function. - fn on_dispute_weight() -> Weight; + fn on_dispute_weight(mdm_info: &MDMWeightOf) -> Weight; /// Manage market resolution of a disputed market. /// @@ -72,6 +81,9 @@ pub trait DisputeApi { market: &MarketOfDisputeApi, ) -> Result, DispatchError>; + /// Return the weight of the `on_resolution` function. + fn on_resolution_weight(mdm_info: &MDMWeightOf) -> Weight; + /// Allow the transfer of funds from the API caller to the API consumer and back. /// This can be based on the final resolution outcome of the market. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. @@ -91,6 +103,9 @@ pub trait DisputeApi { amount: Self::NegativeImbalance, ) -> Result; + /// Return the weight of the `exchange` function. + fn exchange_weight(mdm_info: &MDMWeightOf) -> Weight; + /// Query the future resolution block of a disputed market. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. /// @@ -102,6 +117,9 @@ pub trait DisputeApi { market: &MarketOfDisputeApi, ) -> Result, DispatchError>; + /// Return the weight of the `get_auto_resolve` function. + fn get_auto_resolve_weight(mdm_info: &MDMWeightOf) -> Weight; + /// 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. @@ -110,6 +128,9 @@ pub trait DisputeApi { market: &MarketOfDisputeApi, ) -> Result; + /// Return the weight of the `has_failed` function. + fn has_failed_weight(mdm_info: &MDMWeightOf) -> Weight; + /// Called, when a global dispute is started. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. /// @@ -120,10 +141,16 @@ pub trait DisputeApi { market: &MarketOfDisputeApi, ) -> Result>, DispatchError>; + /// Return the weight of the `on_global_dispute` function. + fn on_global_dispute_weight(mdm_info: &MDMWeightOf) -> Weight; + /// Allow the API consumer to clear storage items of the dispute mechanism. /// This may be called, when the dispute mechanism is no longer needed. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. fn clear(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; + + /// Return the weight of the `clear` function. + fn clear_weight(mdm_info: &MDMWeightOf) -> Weight; } type MarketOfDisputeResolutionApi = Market< diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index cbab9b329..ff3cbd3f1 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -22,11 +22,14 @@ )] #![cfg(feature = "runtime-benchmarks")] -use crate::{market_mock, AuthorizedOutcomeReports, Call, Config, Pallet as Authorized, Pallet}; +use crate::{ + market_mock, AuthorizedOutcomeReports, Call, Config, NegativeImbalanceOf, Pallet as Authorized, + Pallet, +}; use frame_benchmarking::benchmarks; use frame_support::{ dispatch::UnfilteredDispatchable, - traits::{EnsureOrigin, Get}, + traits::{EnsureOrigin, Get, Imbalance}, }; use sp_runtime::traits::Saturating; use zeitgeist_primitives::{ @@ -102,6 +105,77 @@ benchmarks! { Authorized::::on_dispute(&market_id, &market).unwrap(); } + on_resolution_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + frame_system::Pallet::::set_block_number(42u32.into()); + + let now = frame_system::Pallet::::block_number(); + let resolve_at = now.saturating_add(T::CorrectionPeriod::get()); + + let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(0) }; + AuthorizedOutcomeReports::::insert(market_id, report); + }: { + Authorized::::on_resolution(&market_id, &market).unwrap(); + } + + exchange_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + let outcome = OutcomeReport::Scalar(0); + let imb = NegativeImbalanceOf::::zero(); + }: { + Authorized::::exchange(&market_id, &market, &outcome, imb).unwrap(); + } + + get_auto_resolve_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + let now = frame_system::Pallet::::block_number(); + let resolve_at = now.saturating_add(T::CorrectionPeriod::get()); + + let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(0) }; + AuthorizedOutcomeReports::::insert(market_id, report); + }: { + Authorized::::get_auto_resolve(&market_id, &market).unwrap(); + } + + has_failed_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + }: { + Authorized::::has_failed(&market_id, &market).unwrap(); + } + + on_global_dispute_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + }: { + Authorized::::on_global_dispute(&market_id, &market).unwrap(); + } + + clear_weight { + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + let now = frame_system::Pallet::::block_number(); + let resolve_at = now.saturating_add(T::CorrectionPeriod::get()); + + let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(0) }; + AuthorizedOutcomeReports::::insert(market_id, report); + }: { + Authorized::::clear(&market_id, &market).unwrap(); + } + impl_benchmark_test_suite!( Authorized, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index d98cba895..55b26777c 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -49,7 +49,7 @@ mod pallet { use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, types::{ - Asset, AuthorityReport, GlobalDisputeItem, Market, MarketDisputeMechanism, + Asset, AuthorityReport, GlobalDisputeItem, MDMWeight, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport, }, }; @@ -75,6 +75,14 @@ mod pallet { MomentOf, Asset>, >; + pub(crate) type MDMWeightOf = MDMWeight< + MarketIdOf, + ::AccountId, + BalanceOf, + ::BlockNumber, + MomentOf, + Asset>, + >; #[pallet::call] impl Pallet { @@ -216,7 +224,7 @@ mod pallet { Ok(()) } - fn on_dispute_weight() -> Weight { + fn on_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { T::WeightInfo::on_dispute_weight() } @@ -232,6 +240,10 @@ mod pallet { Ok(report.map(|r| r.outcome)) } + fn on_resolution_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::on_resolution_weight() + } + fn exchange( _: &Self::MarketId, market: &MarketOf, @@ -246,6 +258,10 @@ mod pallet { Ok(overall_imbalance) } + fn exchange_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::exchange_weight() + } + fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, @@ -257,6 +273,10 @@ mod pallet { Ok(Self::get_auto_resolve(market_id)) } + fn get_auto_resolve_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::get_auto_resolve_weight() + } + fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, @@ -266,6 +286,10 @@ mod pallet { Ok(false) } + fn has_failed_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::has_failed_weight() + } + fn on_global_dispute( _: &Self::MarketId, market: &MarketOf, @@ -278,6 +302,10 @@ mod pallet { Ok(Vec::new()) } + fn on_global_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::on_global_dispute_weight() + } + fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, @@ -287,6 +315,10 @@ mod pallet { AuthorizedOutcomeReports::::remove(market_id); Ok(()) } + + fn clear_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::clear_weight() + } } impl AuthorizedPalletApi for Pallet where T: Config {} diff --git a/zrml/authorized/src/weights.rs b/zrml/authorized/src/weights.rs index b27ee7161..1754ea7c7 100644 --- a/zrml/authorized/src/weights.rs +++ b/zrml/authorized/src/weights.rs @@ -49,6 +49,12 @@ pub trait WeightInfoZeitgeist { fn authorize_market_outcome_first_report(m: u32) -> Weight; fn authorize_market_outcome_existing_report() -> Weight; fn on_dispute_weight() -> Weight; + fn on_resolution_weight() -> Weight; + fn exchange_weight() -> Weight; + fn get_auto_resolve_weight() -> Weight; + fn has_failed_weight() -> Weight; + fn on_global_dispute_weight() -> Weight; + fn clear_weight() -> Weight; } /// Weight functions for zrml_authorized (automatically generated) @@ -74,4 +80,22 @@ impl WeightInfoZeitgeist for WeightInfo { fn on_dispute_weight() -> Weight { Weight::from_ref_time(0) } + fn on_resolution_weight() -> Weight { + Weight::from_ref_time(0) + } + fn exchange_weight() -> Weight { + Weight::from_ref_time(0) + } + fn get_auto_resolve_weight() -> Weight { + Weight::from_ref_time(0) + } + fn has_failed_weight() -> Weight { + Weight::from_ref_time(0) + } + fn on_global_dispute_weight() -> Weight { + Weight::from_ref_time(0) + } + fn clear_weight() -> Weight { + Weight::from_ref_time(0) + } } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 77ea0d9f3..0e3816d1e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -68,8 +68,8 @@ mod pallet { use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi}, types::{ - Asset, GlobalDisputeItem, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, - OutcomeReport, + Asset, GlobalDisputeItem, MDMWeight, Market, MarketDispute, MarketDisputeMechanism, + MarketStatus, OutcomeReport, }, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -102,6 +102,14 @@ mod pallet { MomentOf, Asset>, >; + pub(crate) type MDMWeightOf = MDMWeight< + MarketIdOf, + ::AccountId, + BalanceOf, + ::BlockNumber, + MomentOf, + Asset>, + >; #[pallet::call] impl Pallet { @@ -561,7 +569,7 @@ mod pallet { Ok(()) } - fn on_dispute_weight() -> Weight { + fn on_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { T::WeightInfo::on_dispute_weight() } @@ -597,6 +605,10 @@ mod pallet { Ok(Some(first)) } + fn on_resolution_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::on_resolution_weight() + } + fn exchange( _: &Self::MarketId, market: &MarketOf, @@ -611,6 +623,10 @@ mod pallet { Ok(overall_imbalance) } + fn exchange_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::exchange_weight() + } + fn get_auto_resolve( _: &Self::MarketId, market: &MarketOf, @@ -622,6 +638,10 @@ mod pallet { Ok(None) } + fn get_auto_resolve_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::get_auto_resolve_weight() + } + fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, @@ -630,6 +650,10 @@ mod pallet { Ok(false) } + fn has_failed_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::has_failed_weight() + } + fn on_global_dispute( _: &Self::MarketId, market: &MarketOf, @@ -641,6 +665,10 @@ mod pallet { Ok(Vec::new()) } + fn on_global_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::on_global_dispute_weight() + } + fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, @@ -650,6 +678,10 @@ mod pallet { let _ = RequestedJurors::::clear_prefix(market_id, u32::max_value(), None); Ok(()) } + + fn clear_weight(_mdm_info: &MDMWeightOf) -> Weight { + T::WeightInfo::clear_weight() + } } impl CourtPalletApi for Pallet where T: Config {} diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index eed9d2298..f50651eee 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -50,6 +50,12 @@ pub trait WeightInfoZeitgeist { fn join_court() -> Weight; fn vote() -> Weight; fn on_dispute_weight() -> Weight; + fn on_resolution_weight() -> Weight; + fn exchange_weight() -> Weight; + fn get_auto_resolve_weight() -> Weight; + fn has_failed_weight() -> Weight; + fn on_global_dispute_weight() -> Weight; + fn clear_weight() -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -80,8 +86,25 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - fn on_dispute_weight() -> Weight { Weight::from_ref_time(0) } + fn on_resolution_weight() -> Weight { + Weight::from_ref_time(0) + } + fn exchange_weight() -> Weight { + Weight::from_ref_time(0) + } + fn get_auto_resolve_weight() -> Weight { + Weight::from_ref_time(0) + } + fn has_failed_weight() -> Weight { + Weight::from_ref_time(0) + } + fn on_global_dispute_weight() -> Weight { + Weight::from_ref_time(0) + } + fn clear_weight() -> Weight { + Weight::from_ref_time(0) + } } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index e07a54a90..cdf73265b 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -64,9 +64,9 @@ mod pallet { constants::MILLISECS_PER_BLOCK, traits::{DisputeApi, DisputeResolutionApi, Swaps, ZeitgeistAssetManager}, types::{ - Asset, Bond, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, - MarketPeriod, MarketStatus, MarketType, MultiHash, OldMarketDispute, OutcomeReport, - Report, ScalarPosition, ScoringRule, SubsidyUntil, + Asset, Bond, Deadlines, MDMWeight, Market, MarketBonds, MarketCreation, + MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, MultiHash, + OldMarketDispute, OutcomeReport, Report, ScalarPosition, ScoringRule, SubsidyUntil, }, }; #[cfg(feature = "with-global-disputes")] @@ -618,8 +618,8 @@ mod pallet { /// Complexity: `O(n)`, where `n` is the number of outstanding disputes. #[pallet::weight( T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_weight().saturating_add( - T::SimpleDisputes::on_dispute_weight() + T::Court::on_dispute_weight(&MDMWeight::Default).saturating_add( + T::SimpleDisputes::on_dispute_weight(&MDMWeight::Default) ) ) )] @@ -636,17 +636,26 @@ mod pallet { let overweight = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_dispute(&market_id, &market)?; - T::Court::on_dispute_weight() - .saturating_add(T::SimpleDisputes::on_dispute_weight()) + T::Court::on_dispute_weight(&MDMWeight::Court).saturating_add( + T::SimpleDisputes::on_dispute_weight(&MDMWeight::SimpleDisputes { + market_id, + market: market.clone(), + }), + ) } MarketDisputeMechanism::Court => { T::Court::on_dispute(&market_id, &market)?; - T::Authorized::on_dispute_weight() - .saturating_add(T::SimpleDisputes::on_dispute_weight()) + T::Authorized::on_dispute_weight(&MDMWeight::Authorized).saturating_add( + T::SimpleDisputes::on_dispute_weight(&MDMWeight::SimpleDisputes { + market_id, + market: market.clone(), + }), + ) } MarketDisputeMechanism::SimpleDisputes => { T::SimpleDisputes::on_dispute(&market_id, &market)?; - T::Court::on_dispute_weight().saturating_add(T::Authorized::on_dispute_weight()) + T::Court::on_dispute_weight(&MDMWeight::Court) + .saturating_add(T::Authorized::on_dispute_weight(&MDMWeight::Authorized)) } }; @@ -663,8 +672,12 @@ mod pallet { Self::deposit_event(Event::MarketDisputed(market_id, MarketStatus::Disputed)); let full_weight = T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_weight() - .saturating_add(T::SimpleDisputes::on_dispute_weight()), + T::Court::on_dispute_weight(&MDMWeight::Court).saturating_add( + T::SimpleDisputes::on_dispute_weight(&MDMWeight::SimpleDisputes { + market_id, + market, + }), + ), ); Ok((Some(full_weight.saturating_sub(overweight))).into()) @@ -1457,6 +1470,8 @@ mod pallet { let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; + // TODO(#782): use multiple benchmarks paths for different dispute mechanisms + let has_failed = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::has_failed(&market_id, &market)? @@ -2249,6 +2264,8 @@ mod pallet { /// The dispute mechanism is intended to clear its own storage here. fn clear_dispute_mechanism(market_id: &MarketIdOf) -> DispatchResult { let market = >::market(market_id)?; + + // TODO(#782): use multiple benchmarks paths for different dispute mechanisms match market.dispute_mechanism { MarketDisputeMechanism::Authorized => T::Authorized::clear(market_id, &market)?, MarketDisputeMechanism::Court => T::Court::clear(market_id, &market)?, @@ -2546,6 +2563,7 @@ mod pallet { let imbalance_left = Self::settle_bonds(market_id, market, &resolved_outcome, report)?; + // TODO(#782): use multiple benchmarks paths for different dispute mechanisms let remainder = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::exchange(market_id, market, &resolved_outcome, imbalance_left)? diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index 6fd425a0f..6f64cc8d7 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -25,12 +25,32 @@ use crate::Pallet as SimpleDisputes; use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller, Vec}; -use frame_support::{dispatch::RawOrigin, traits::Get}; +use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_support::{ + dispatch::RawOrigin, + traits::{Get, Imbalance}, +}; use orml_traits::MultiCurrency; use sp_runtime::traits::{One, Saturating}; use zrml_market_commons::MarketCommonsPalletApi; +fn fill_disputes(market_id: MarketIdOf, d: u32) { + for i in 0..d { + let now = >::block_number(); + let disputor = account("disputor", i, 0); + let bond = default_outcome_bond::(i as usize); + T::AssetManager::deposit(Asset::Ztg, &disputor, bond).unwrap(); + let outcome = OutcomeReport::Scalar((2 + i).into()); + SimpleDisputes::::suggest_outcome( + RawOrigin::Signed(disputor).into(), + market_id, + outcome, + ) + .unwrap(); + >::set_block_number(now.saturating_add(T::BlockNumber::one())); + } +} + benchmarks! { suggest_outcome { let d in 1..(T::MaxDisputes::get() - 1); @@ -42,22 +62,8 @@ benchmarks! { let market = market_mock::(); T::MarketCommons::push_market(market.clone()).unwrap(); - let mut now; - - let mut disputes = Vec::new(); - for i in 0..d { - now = >::block_number(); - - let disputor = account("disputor", i, 0); - let last_dispute = MarketDispute { - at: now, - by: disputor, - outcome: OutcomeReport::Scalar((2 + i).into()), - bond: default_outcome_bond::(i as usize), - }; - disputes.push(last_dispute); - >::set_block_number(now.saturating_add(T::BlockNumber::one())); - } + fill_disputes::(market_id, d); + let disputes = Disputes::::get(market_id); let last_dispute = disputes.last().unwrap(); let auto_resolve = last_dispute.at.saturating_add(market.deadlines.dispute_duration); for i in 0..r { @@ -67,9 +73,6 @@ benchmarks! { let now = >::block_number(); - let bounded_vec = >::try_from(disputes).unwrap(); - Disputes::::insert(market_id, bounded_vec); - let dispute_duration_ends_at_block = now.saturating_add(market.deadlines.dispute_duration); for i in 0..e { @@ -90,6 +93,81 @@ benchmarks! { SimpleDisputes::::on_dispute(&market_id, &market).unwrap(); } + on_resolution_weight { + let d in 1..T::MaxDisputes::get(); + + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + fill_disputes::(market_id, d); + }: { + SimpleDisputes::::on_resolution(&market_id, &market).unwrap(); + } + + exchange_weight { + let d in 1..T::MaxDisputes::get(); + + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + fill_disputes::(market_id, d); + + let outcome = OutcomeReport::Scalar(1); + let imb = NegativeImbalanceOf::::zero(); + }: { + SimpleDisputes::::exchange(&market_id, &market, &outcome, imb).unwrap(); + } + + get_auto_resolve_weight { + let d in 1..T::MaxDisputes::get(); + + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + fill_disputes::(market_id, d); + }: { + SimpleDisputes::::get_auto_resolve(&market_id, &market).unwrap(); + } + + has_failed_weight { + let d in 1..T::MaxDisputes::get(); + + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + fill_disputes::(market_id, d); + }: { + SimpleDisputes::::has_failed(&market_id, &market).unwrap(); + } + + on_global_dispute_weight { + let d in 1..T::MaxDisputes::get(); + + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + fill_disputes::(market_id, d); + }: { + SimpleDisputes::::on_global_dispute(&market_id, &market).unwrap(); + } + + clear_weight { + let d in 1..T::MaxDisputes::get(); + + let market_id = 0u32.into(); + let market = market_mock::(); + T::MarketCommons::push_market(market.clone()).unwrap(); + + fill_disputes::(market_id, d); + }: { + SimpleDisputes::::clear(&market_id, &market).unwrap(); + } + impl_benchmark_test_suite!( SimpleDisputes, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index 303d11d2b..d124d1721 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -33,8 +33,8 @@ pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeResolutionApi, ZeitgeistAssetManager}, types::{ - Asset, GlobalDisputeItem, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, - OutcomeReport, Report, + Asset, GlobalDisputeItem, MDMWeight, Market, MarketDispute, MarketDisputeMechanism, + MarketStatus, OutcomeReport, Report, }, }; @@ -124,6 +124,14 @@ mod pallet { MomentOf, Asset>, >; + pub(crate) type MDMWeightOf = MDMWeight< + MarketIdOf, + ::AccountId, + BalanceOf, + ::BlockNumber, + MomentOf, + Asset>, + >; pub(crate) type DisputesOf = BoundedVec< MarketDispute< ::AccountId, @@ -295,7 +303,7 @@ mod pallet { Ok(()) } - fn on_dispute_weight() -> Weight { + fn on_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { T::WeightInfo::on_dispute_weight() } @@ -320,6 +328,16 @@ mod pallet { Ok(Some(last_dispute.outcome.clone())) } + fn on_resolution_weight(mdm_info: &MDMWeightOf) -> Weight { + match mdm_info { + MDMWeight::SimpleDisputes { market_id, market: _ } => { + let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; + T::WeightInfo::on_resolution_weight(disputes_len) + } + _ => T::WeightInfo::on_resolution_weight(T::MaxDisputes::get()), + } + } + fn exchange( market_id: &Self::MarketId, market: &MarketOf, @@ -374,6 +392,16 @@ mod pallet { Ok(overall_imbalance) } + fn exchange_weight(mdm_info: &MDMWeightOf) -> Weight { + match mdm_info { + MDMWeight::SimpleDisputes { market_id, market: _ } => { + let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; + T::WeightInfo::exchange_weight(disputes_len) + } + _ => T::WeightInfo::exchange_weight(T::MaxDisputes::get()), + } + } + fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, @@ -386,6 +414,16 @@ mod pallet { Ok(Self::get_auto_resolve(disputes.as_slice(), market)) } + fn get_auto_resolve_weight(mdm_info: &MDMWeightOf) -> Weight { + match mdm_info { + MDMWeight::SimpleDisputes { market_id, market: _ } => { + let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; + T::WeightInfo::get_auto_resolve_weight(disputes_len) + } + _ => T::WeightInfo::get_auto_resolve_weight(T::MaxDisputes::get()), + } + } + fn has_failed( market_id: &Self::MarketId, market: &MarketOf, @@ -398,6 +436,16 @@ mod pallet { Ok(disputes.len() == T::MaxDisputes::get() as usize) } + fn has_failed_weight(mdm_info: &MDMWeightOf) -> Weight { + match mdm_info { + MDMWeight::SimpleDisputes { market_id, market: _ } => { + let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; + T::WeightInfo::has_failed_weight(disputes_len) + } + _ => T::WeightInfo::has_failed_weight(T::MaxDisputes::get()), + } + } + fn on_global_dispute( market_id: &Self::MarketId, market: &MarketOf, @@ -417,6 +465,16 @@ mod pallet { .collect()) } + fn on_global_dispute_weight(mdm_info: &MDMWeightOf) -> Weight { + match mdm_info { + MDMWeight::SimpleDisputes { market_id, market: _ } => { + let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; + T::WeightInfo::on_global_dispute_weight(disputes_len) + } + _ => T::WeightInfo::on_global_dispute_weight(T::MaxDisputes::get()), + } + } + fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, @@ -436,6 +494,16 @@ mod pallet { } Ok(()) } + + fn clear_weight(mdm_info: &MDMWeightOf) -> Weight { + match mdm_info { + MDMWeight::SimpleDisputes { market_id, market: _ } => { + let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; + T::WeightInfo::clear_weight(disputes_len) + } + _ => T::WeightInfo::clear_weight(T::MaxDisputes::get()), + } + } } impl SimpleDisputesPalletApi for Pallet where T: Config {} diff --git a/zrml/simple-disputes/src/weights.rs b/zrml/simple-disputes/src/weights.rs index 2143935cc..f4233871d 100644 --- a/zrml/simple-disputes/src/weights.rs +++ b/zrml/simple-disputes/src/weights.rs @@ -47,6 +47,12 @@ use frame_support::{traits::Get, weights::Weight}; pub trait WeightInfoZeitgeist { fn suggest_outcome(d: u32, r: u32, e: u32) -> Weight; fn on_dispute_weight() -> Weight; + fn on_resolution_weight(d: u32) -> Weight; + fn exchange_weight(d: u32) -> Weight; + fn get_auto_resolve_weight(d: u32) -> Weight; + fn has_failed_weight(d: u32) -> Weight; + fn on_global_dispute_weight(d: u32) -> Weight; + fn clear_weight(d: u32) -> Weight; } /// Weight functions for zrml_simple_disputes (automatically generated) @@ -71,4 +77,56 @@ impl WeightInfoZeitgeist for WeightInfo { fn on_dispute_weight() -> Weight { Weight::from_ref_time(0) } + // Storage: SimpleDisputes Disputes (r:1 w:0) + fn on_resolution_weight(d: u32) -> Weight { + Weight::from_ref_time(5_464_000) + // Standard Error: 3_000 + .saturating_add(Weight::from_ref_time(210_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: SimpleDisputes Disputes (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn exchange_weight(d: u32) -> Weight { + Weight::from_ref_time(18_573_000) + // Standard Error: 14_000 + .saturating_add(Weight::from_ref_time(19_710_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(d.into()))) + } + // Storage: SimpleDisputes Disputes (r:1 w:0) + fn get_auto_resolve_weight(d: u32) -> Weight { + Weight::from_ref_time(5_535_000) + // Standard Error: 3_000 + .saturating_add(Weight::from_ref_time(145_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: SimpleDisputes Disputes (r:1 w:0) + fn has_failed_weight(d: u32) -> Weight { + Weight::from_ref_time(5_685_000) + // Standard Error: 2_000 + .saturating_add(Weight::from_ref_time(117_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: SimpleDisputes Disputes (r:1 w:0) + fn on_global_dispute_weight(d: u32) -> Weight { + Weight::from_ref_time(5_815_000) + // Standard Error: 2_000 + .saturating_add(Weight::from_ref_time(66_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: SimpleDisputes Disputes (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn clear_weight(d: u32) -> Weight { + Weight::from_ref_time(15_958_000) + // Standard Error: 17_000 + .saturating_add(Weight::from_ref_time(13_085_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(d.into()))) + } } From f48a0a36a50d65173ea6bed439f4c038ba97af31 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 18 Apr 2023 10:42:58 +0200 Subject: [PATCH 256/368] add mdm weights to on_resolution --- primitives/src/traits/dispute_api.rs | 14 ++--- zrml/authorized/src/lib.rs | 14 ++--- zrml/court/src/lib.rs | 14 ++--- zrml/prediction-markets/src/lib.rs | 94 +++++++++++++++++++--------- zrml/simple-disputes/src/lib.rs | 14 ++--- 5 files changed, 94 insertions(+), 56 deletions(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index f67281e53..edabaf2f7 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -65,7 +65,7 @@ pub trait DisputeApi { fn on_dispute(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; /// Return the weight of the `on_dispute` function. - fn on_dispute_weight(mdm_info: &MDMWeightOf) -> Weight; + fn on_dispute_weight(mdm_info: MDMWeightOf) -> Weight; /// Manage market resolution of a disputed market. /// @@ -82,7 +82,7 @@ pub trait DisputeApi { ) -> Result, DispatchError>; /// Return the weight of the `on_resolution` function. - fn on_resolution_weight(mdm_info: &MDMWeightOf) -> Weight; + fn on_resolution_weight(mdm_info: MDMWeightOf) -> Weight; /// Allow the transfer of funds from the API caller to the API consumer and back. /// This can be based on the final resolution outcome of the market. @@ -104,7 +104,7 @@ pub trait DisputeApi { ) -> Result; /// Return the weight of the `exchange` function. - fn exchange_weight(mdm_info: &MDMWeightOf) -> Weight; + fn exchange_weight(mdm_info: MDMWeightOf) -> Weight; /// Query the future resolution block of a disputed market. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. @@ -118,7 +118,7 @@ pub trait DisputeApi { ) -> Result, DispatchError>; /// Return the weight of the `get_auto_resolve` function. - fn get_auto_resolve_weight(mdm_info: &MDMWeightOf) -> Weight; + fn get_auto_resolve_weight(mdm_info: MDMWeightOf) -> Weight; /// Returns `true` if the market dispute mechanism /// was unable to come to a conclusion. @@ -129,7 +129,7 @@ pub trait DisputeApi { ) -> Result; /// Return the weight of the `has_failed` function. - fn has_failed_weight(mdm_info: &MDMWeightOf) -> Weight; + fn has_failed_weight(mdm_info: MDMWeightOf) -> Weight; /// Called, when a global dispute is started. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. @@ -142,7 +142,7 @@ pub trait DisputeApi { ) -> Result>, DispatchError>; /// Return the weight of the `on_global_dispute` function. - fn on_global_dispute_weight(mdm_info: &MDMWeightOf) -> Weight; + fn on_global_dispute_weight(mdm_info: MDMWeightOf) -> Weight; /// Allow the API consumer to clear storage items of the dispute mechanism. /// This may be called, when the dispute mechanism is no longer needed. @@ -150,7 +150,7 @@ pub trait DisputeApi { fn clear(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; /// Return the weight of the `clear` function. - fn clear_weight(mdm_info: &MDMWeightOf) -> Weight; + fn clear_weight(mdm_info: MDMWeightOf) -> Weight; } type MarketOfDisputeResolutionApi = Market< diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 55b26777c..ac1929da8 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -224,7 +224,7 @@ mod pallet { Ok(()) } - fn on_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn on_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::on_dispute_weight() } @@ -240,7 +240,7 @@ mod pallet { Ok(report.map(|r| r.outcome)) } - fn on_resolution_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn on_resolution_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::on_resolution_weight() } @@ -258,7 +258,7 @@ mod pallet { Ok(overall_imbalance) } - fn exchange_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn exchange_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::exchange_weight() } @@ -273,7 +273,7 @@ mod pallet { Ok(Self::get_auto_resolve(market_id)) } - fn get_auto_resolve_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn get_auto_resolve_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::get_auto_resolve_weight() } @@ -286,7 +286,7 @@ mod pallet { Ok(false) } - fn has_failed_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn has_failed_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::has_failed_weight() } @@ -302,7 +302,7 @@ mod pallet { Ok(Vec::new()) } - fn on_global_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn on_global_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::on_global_dispute_weight() } @@ -316,7 +316,7 @@ mod pallet { Ok(()) } - fn clear_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn clear_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::clear_weight() } } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 0e3816d1e..aca008ad3 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -569,7 +569,7 @@ mod pallet { Ok(()) } - fn on_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn on_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::on_dispute_weight() } @@ -605,7 +605,7 @@ mod pallet { Ok(Some(first)) } - fn on_resolution_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn on_resolution_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::on_resolution_weight() } @@ -623,7 +623,7 @@ mod pallet { Ok(overall_imbalance) } - fn exchange_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn exchange_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::exchange_weight() } @@ -638,7 +638,7 @@ mod pallet { Ok(None) } - fn get_auto_resolve_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn get_auto_resolve_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::get_auto_resolve_weight() } @@ -650,7 +650,7 @@ mod pallet { Ok(false) } - fn has_failed_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn has_failed_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::has_failed_weight() } @@ -665,7 +665,7 @@ mod pallet { Ok(Vec::new()) } - fn on_global_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn on_global_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::on_global_dispute_weight() } @@ -679,7 +679,7 @@ mod pallet { Ok(()) } - fn clear_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn clear_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::clear_weight() } } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index cdf73265b..7c11765b7 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -618,8 +618,8 @@ mod pallet { /// Complexity: `O(n)`, where `n` is the number of outstanding disputes. #[pallet::weight( T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_weight(&MDMWeight::Default).saturating_add( - T::SimpleDisputes::on_dispute_weight(&MDMWeight::Default) + T::Court::on_dispute_weight(MDMWeight::Default).saturating_add( + T::SimpleDisputes::on_dispute_weight(MDMWeight::Default) ) ) )] @@ -636,8 +636,8 @@ mod pallet { let overweight = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_dispute(&market_id, &market)?; - T::Court::on_dispute_weight(&MDMWeight::Court).saturating_add( - T::SimpleDisputes::on_dispute_weight(&MDMWeight::SimpleDisputes { + T::Court::on_dispute_weight(MDMWeight::Court).saturating_add( + T::SimpleDisputes::on_dispute_weight(MDMWeight::SimpleDisputes { market_id, market: market.clone(), }), @@ -645,8 +645,8 @@ mod pallet { } MarketDisputeMechanism::Court => { T::Court::on_dispute(&market_id, &market)?; - T::Authorized::on_dispute_weight(&MDMWeight::Authorized).saturating_add( - T::SimpleDisputes::on_dispute_weight(&MDMWeight::SimpleDisputes { + T::Authorized::on_dispute_weight(MDMWeight::Authorized).saturating_add( + T::SimpleDisputes::on_dispute_weight(MDMWeight::SimpleDisputes { market_id, market: market.clone(), }), @@ -654,8 +654,8 @@ mod pallet { } MarketDisputeMechanism::SimpleDisputes => { T::SimpleDisputes::on_dispute(&market_id, &market)?; - T::Court::on_dispute_weight(&MDMWeight::Court) - .saturating_add(T::Authorized::on_dispute_weight(&MDMWeight::Authorized)) + T::Court::on_dispute_weight(MDMWeight::Court) + .saturating_add(T::Authorized::on_dispute_weight(MDMWeight::Authorized)) } }; @@ -672,8 +672,8 @@ mod pallet { Self::deposit_event(Event::MarketDisputed(market_id, MarketStatus::Disputed)); let full_weight = T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_weight(&MDMWeight::Court).saturating_add( - T::SimpleDisputes::on_dispute_weight(&MDMWeight::SimpleDisputes { + T::Court::on_dispute_weight(MDMWeight::Court).saturating_add( + T::SimpleDisputes::on_dispute_weight(MDMWeight::SimpleDisputes { market_id, market, }), @@ -2555,33 +2555,51 @@ mod pallet { fn resolve_disputed_market( market_id: &MarketIdOf, market: &MarketOf, - ) -> Result { + ) -> Result<(OutcomeReport, Weight), DispatchError> { let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; + let mut weight = Weight::zero(); - let resolved_outcome: OutcomeReport = + let (resolved_outcome, w): (OutcomeReport, Weight) = Self::get_resolved_outcome(market_id, market, &report.outcome)?; + weight = weight.saturating_add(w); let imbalance_left = Self::settle_bonds(market_id, market, &resolved_outcome, report)?; - // TODO(#782): use multiple benchmarks paths for different dispute mechanisms let remainder = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::exchange(market_id, market, &resolved_outcome, imbalance_left)? + let remainder = T::Authorized::exchange( + market_id, + market, + &resolved_outcome, + imbalance_left, + )?; + weight = weight + .saturating_add(T::Authorized::exchange_weight(MDMWeight::Authorized)); + remainder } MarketDisputeMechanism::Court => { - T::Court::exchange(market_id, market, &resolved_outcome, imbalance_left)? + let remainder = + T::Court::exchange(market_id, market, &resolved_outcome, imbalance_left)?; + weight = weight.saturating_add(T::Court::exchange_weight(MDMWeight::Court)); + remainder + } + MarketDisputeMechanism::SimpleDisputes => { + let remainder = T::SimpleDisputes::exchange( + market_id, + market, + &resolved_outcome, + imbalance_left, + )?; + weight = weight.saturating_add(T::SimpleDisputes::exchange_weight( + MDMWeight::SimpleDisputes { market_id: *market_id, market: market.clone() }, + )); + remainder } - MarketDisputeMechanism::SimpleDisputes => T::SimpleDisputes::exchange( - market_id, - market, - &resolved_outcome, - imbalance_left, - )?, }; T::Slash::on_unbalanced(remainder); - Ok(resolved_outcome) + Ok((resolved_outcome, weight)) } /// Get the outcome the market should resolve to. @@ -2589,31 +2607,46 @@ mod pallet { market_id: &MarketIdOf, market: &MarketOf, reported_outcome: &OutcomeReport, - ) -> Result { + ) -> Result<(OutcomeReport, Weight), DispatchError> { let mut resolved_outcome_option = None; + let mut weight = Weight::zero(); #[cfg(feature = "with-global-disputes")] if let Some(o) = T::GlobalDisputes::determine_voting_winner(market_id) { resolved_outcome_option = Some(o); } - // TODO(#782): use multiple benchmarks paths for different dispute mechanisms - // Try to get the outcome of the MDM. If the MDM failed to resolve, default to // the oracle's report. if resolved_outcome_option.is_none() { resolved_outcome_option = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { + weight = weight.saturating_add(T::Authorized::on_resolution_weight( + MDMWeight::Authorized, + )); T::Authorized::on_resolution(market_id, market)? } - MarketDisputeMechanism::Court => T::Court::on_resolution(market_id, market)?, + MarketDisputeMechanism::Court => { + weight = + weight.saturating_add(T::Court::on_resolution_weight(MDMWeight::Court)); + T::Court::on_resolution(market_id, market)? + } MarketDisputeMechanism::SimpleDisputes => { + weight = weight.saturating_add(T::SimpleDisputes::on_resolution_weight( + MDMWeight::SimpleDisputes { + market_id: *market_id, + market: market.clone(), + }, + )); T::SimpleDisputes::on_resolution(market_id, market)? } }; } - Ok(resolved_outcome_option.unwrap_or_else(|| reported_outcome.clone())) + let resolved_outcome = + resolved_outcome_option.unwrap_or_else(|| reported_outcome.clone()); + + Ok((resolved_outcome, weight)) } /// Manage the outstanding bonds (oracle, outsider, dispute) of the market. @@ -2693,7 +2726,12 @@ mod pallet { let resolved_outcome = match market.status { MarketStatus::Reported => Self::resolve_reported_market(market_id, market)?, - MarketStatus::Disputed => Self::resolve_disputed_market(market_id, market)?, + MarketStatus::Disputed => { + let (resolved_outcome, weight) = + Self::resolve_disputed_market(market_id, market)?; + total_weight = total_weight.saturating_add(weight); + resolved_outcome + } _ => return Err(Error::::InvalidMarketStatus.into()), }; let clean_up_weight = Self::clean_up_pool(market, market_id, &resolved_outcome)?; diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index d124d1721..c96ff8280 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -303,7 +303,7 @@ mod pallet { Ok(()) } - fn on_dispute_weight(_mdm_info: &MDMWeightOf) -> Weight { + fn on_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { T::WeightInfo::on_dispute_weight() } @@ -328,7 +328,7 @@ mod pallet { Ok(Some(last_dispute.outcome.clone())) } - fn on_resolution_weight(mdm_info: &MDMWeightOf) -> Weight { + fn on_resolution_weight(mdm_info: MDMWeightOf) -> Weight { match mdm_info { MDMWeight::SimpleDisputes { market_id, market: _ } => { let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; @@ -392,7 +392,7 @@ mod pallet { Ok(overall_imbalance) } - fn exchange_weight(mdm_info: &MDMWeightOf) -> Weight { + fn exchange_weight(mdm_info: MDMWeightOf) -> Weight { match mdm_info { MDMWeight::SimpleDisputes { market_id, market: _ } => { let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; @@ -414,7 +414,7 @@ mod pallet { Ok(Self::get_auto_resolve(disputes.as_slice(), market)) } - fn get_auto_resolve_weight(mdm_info: &MDMWeightOf) -> Weight { + fn get_auto_resolve_weight(mdm_info: MDMWeightOf) -> Weight { match mdm_info { MDMWeight::SimpleDisputes { market_id, market: _ } => { let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; @@ -436,7 +436,7 @@ mod pallet { Ok(disputes.len() == T::MaxDisputes::get() as usize) } - fn has_failed_weight(mdm_info: &MDMWeightOf) -> Weight { + fn has_failed_weight(mdm_info: MDMWeightOf) -> Weight { match mdm_info { MDMWeight::SimpleDisputes { market_id, market: _ } => { let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; @@ -465,7 +465,7 @@ mod pallet { .collect()) } - fn on_global_dispute_weight(mdm_info: &MDMWeightOf) -> Weight { + fn on_global_dispute_weight(mdm_info: MDMWeightOf) -> Weight { match mdm_info { MDMWeight::SimpleDisputes { market_id, market: _ } => { let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; @@ -495,7 +495,7 @@ mod pallet { Ok(()) } - fn clear_weight(mdm_info: &MDMWeightOf) -> Weight { + fn clear_weight(mdm_info: MDMWeightOf) -> Weight { match mdm_info { MDMWeight::SimpleDisputes { market_id, market: _ } => { let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; From cbe528a5b795c6e98eef8dcfcd8fb4eaba52a285 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 18 Apr 2023 11:28:47 +0200 Subject: [PATCH 257/368] add tests for pm --- zrml/prediction-markets/src/lib.rs | 2 +- zrml/prediction-markets/src/tests.rs | 164 +++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 7c11765b7..0ae48662b 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -2603,7 +2603,7 @@ mod pallet { } /// Get the outcome the market should resolve to. - fn get_resolved_outcome( + pub(crate) fn get_resolved_outcome( market_id: &MarketIdOf, market: &MarketOf, reported_outcome: &OutcomeReport, diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 8d76e7e49..1068ad9b9 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -2697,6 +2697,170 @@ fn dispute_fails_disputed_already() { }); } +#[test] +fn dispute_fails_if_market_not_reported() { + ExtBuilder::default().build().execute_with(|| { + let end = 2; + assert_ok!(PredictionMarkets::create_market( + Origin::signed(ALICE), + Asset::Ztg, + BOB, + MarketPeriod::Block(0..end), + get_deadlines(), + gen_metadata(2), + MarketCreation::Permissionless, + MarketType::Categorical(::MinCategories::get()), + MarketDisputeMechanism::Authorized, + ScoringRule::CPMM, + )); + + // Run to the end of the trading phase. + let market = MarketCommons::market(&0).unwrap(); + let grace_period = end + market.deadlines.grace_period; + run_to_block(grace_period + 1); + + // no report happening here... + + let dispute_at = grace_period + 2; + run_to_block(dispute_at); + + assert_noop!( + PredictionMarkets::dispute(Origin::signed(CHARLIE), 0), + Error::::InvalidMarketStatus, + ); + }); +} + +#[test] +fn dispute_reserves_dispute_bond() { + ExtBuilder::default().build().execute_with(|| { + let end = 2; + assert_ok!(PredictionMarkets::create_market( + Origin::signed(ALICE), + Asset::Ztg, + BOB, + MarketPeriod::Block(0..end), + get_deadlines(), + gen_metadata(2), + MarketCreation::Permissionless, + MarketType::Categorical(::MinCategories::get()), + MarketDisputeMechanism::Authorized, + ScoringRule::CPMM, + )); + + // Run to the end of the trading phase. + let market = MarketCommons::market(&0).unwrap(); + let grace_period = end + market.deadlines.grace_period; + run_to_block(grace_period + 1); + + assert_ok!(PredictionMarkets::report( + Origin::signed(BOB), + 0, + OutcomeReport::Categorical(1) + )); + + let dispute_at = grace_period + 2; + run_to_block(dispute_at); + + let free_charlie_before = Balances::free_balance(CHARLIE); + let reserved_charlie = Balances::reserved_balance(CHARLIE); + assert_eq!(reserved_charlie, 0); + + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + + let free_charlie_after = Balances::free_balance(CHARLIE); + assert_eq!(free_charlie_before - free_charlie_after, DisputeBond::get()); + + let reserved_charlie = Balances::reserved_balance(CHARLIE); + assert_eq!(reserved_charlie, DisputeBond::get()); + }); +} + +#[test] +fn dispute_updates_market() { + ExtBuilder::default().build().execute_with(|| { + let end = 2; + assert_ok!(PredictionMarkets::create_market( + Origin::signed(ALICE), + Asset::Ztg, + BOB, + MarketPeriod::Block(0..end), + get_deadlines(), + gen_metadata(2), + MarketCreation::Permissionless, + MarketType::Categorical(::MinCategories::get()), + MarketDisputeMechanism::Authorized, + ScoringRule::CPMM, + )); + + // Run to the end of the trading phase. + let market = MarketCommons::market(&0).unwrap(); + let grace_period = end + market.deadlines.grace_period; + run_to_block(grace_period + 1); + + assert_ok!(PredictionMarkets::report( + Origin::signed(BOB), + 0, + OutcomeReport::Categorical(1) + )); + + let dispute_at = grace_period + 2; + run_to_block(dispute_at); + + let market = MarketCommons::market(&0).unwrap(); + assert_eq!(market.status, MarketStatus::Reported); + assert_eq!(market.bonds.dispute, None); + + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + + let market = MarketCommons::market(&0).unwrap(); + assert_eq!(market.status, MarketStatus::Disputed); + assert_eq!( + market.bonds.dispute, + Some(Bond { who: CHARLIE, value: DisputeBond::get(), is_settled: false }) + ); + }); +} + +#[test] +fn dispute_emits_event() { + ExtBuilder::default().build().execute_with(|| { + let end = 2; + assert_ok!(PredictionMarkets::create_market( + Origin::signed(ALICE), + Asset::Ztg, + BOB, + MarketPeriod::Block(0..end), + get_deadlines(), + gen_metadata(2), + MarketCreation::Permissionless, + MarketType::Categorical(::MinCategories::get()), + MarketDisputeMechanism::Authorized, + ScoringRule::CPMM, + )); + + // Run to the end of the trading phase. + let market = MarketCommons::market(&0).unwrap(); + let grace_period = end + market.deadlines.grace_period; + run_to_block(grace_period + 1); + + assert_ok!(PredictionMarkets::report( + Origin::signed(BOB), + 0, + OutcomeReport::Categorical(1) + )); + + let dispute_at = grace_period + 2; + run_to_block(dispute_at); + + assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), 0,)); + + System::assert_last_event( + Event::MarketDisputed(0u32.into(), MarketStatus::Disputed).into(), + ); + }); +} + #[test] fn it_allows_anyone_to_report_an_unreported_market() { ExtBuilder::default().build().execute_with(|| { From 7ab62c48c39b380b6edcc51a747d4cd0490f9679 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 18 Apr 2023 14:33:37 +0200 Subject: [PATCH 258/368] modify migration logs, fix try-runtime --- zrml/prediction-markets/src/migrations.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/zrml/prediction-markets/src/migrations.rs b/zrml/prediction-markets/src/migrations.rs index 7e45d23c7..68f126e83 100644 --- a/zrml/prediction-markets/src/migrations.rs +++ b/zrml/prediction-markets/src/migrations.rs @@ -122,12 +122,6 @@ impl OnRuntimeUpgrade for AddOutsiderAn if let Some(first_dispute) = old_disputes.first() { let OldMarketDispute { at: _, by, outcome: _ } = first_dispute; dispute_bond = Some(Bond::new(by.clone(), T::DisputeBond::get())); - } else { - log::warn!( - "MoveDataToSimpleDisputes: Could not find first dispute for market id \ - {:?}", - market_id - ); } } @@ -417,8 +411,8 @@ use frame_support::dispatch::EncodeLike; use sp_runtime::SaturatedConversion; use zeitgeist_primitives::types::{MarketDispute, OldMarketDispute}; -const PREDICTION_MARKETS_REQUIRED_STORAGE_VERSION: u16 = 7; -const PREDICTION_MARKETS_NEXT_STORAGE_VERSION: u16 = 8; +const PREDICTION_MARKETS_REQUIRED_STORAGE_VERSION: u16 = 6; +const PREDICTION_MARKETS_NEXT_STORAGE_VERSION: u16 = 7; #[cfg(feature = "try-runtime")] type OldDisputesOf = frame_support::BoundedVec< @@ -503,7 +497,9 @@ where &old_dispute.by, ); if reserved_balance < bond.saturated_into::().saturated_into() { - log::error!( + // warns for battery station market id 386 + // https://discord.com/channels/737780518313000960/817041223201587230/958682619413934151 + log::warn!( "MoveDataToSimpleDisputes: Could not unreserve {:?} for {:?} because \ reserved balance is only {:?}. Market id: {:?}", bond, From 47061f103943a8ab187b1dce85232cd8b4efad8f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 19 Apr 2023 12:15:31 +0200 Subject: [PATCH 259/368] adjust tests for binary search --- zrml/court/src/benchmarks.rs | 23 +++++++--- zrml/court/src/lib.rs | 13 ++++-- zrml/court/src/tests.rs | 33 ++++++++++---- zrml/prediction-markets/src/tests.rs | 67 +++++++++++++++++----------- 4 files changed, 91 insertions(+), 45 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index d99341f12..31f97fd92 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -167,7 +167,10 @@ where ); let draw = Draw { juror, vote: Vote::Drawn, weight: 1u32, slashable: T::MinJurorStake::get() }; - draws.try_push(draw).unwrap(); + let index = draws + .binary_search_by_key(&draw.juror, |draw| draw.juror.clone()) + .unwrap_or_else(|j| j); + draws.try_insert(index, draw).unwrap(); } >::insert(market_id, draws); Ok(()) @@ -240,12 +243,15 @@ benchmarks! { let mut draws = >::get(market_id); let draws_len = draws.len(); - draws[draws_len.saturating_sub(1usize)] = Draw { + draws.remove(0); + let draw = Draw { juror: caller.clone(), vote: Vote::Drawn, weight: 1u32, - slashable: >::zero() + slashable: >::zero(), }; + let index = draws.binary_search_by_key(&caller, |draw| draw.juror.clone()).unwrap_or_else(|j| j); + draws.try_insert(index, draw).unwrap(); >::insert(market_id, draws); >::set_block_number(pre_vote + 1u64.saturated_into::()); @@ -281,13 +287,16 @@ benchmarks! { let commitment = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); let mut draws = >::get(market_id); + draws.remove(0); let draws_len = draws.len(); - draws[draws_len.saturating_sub(1usize)] = Draw { + let index = draws.binary_search_by_key(&denounced_juror, |draw| draw.juror.clone()).unwrap_or_else(|j| j); + let draw = Draw { juror: denounced_juror, vote: Vote::Secret { commitment }, weight: 1u32, slashable: T::MinJurorStake::get(), }; + draws.try_insert(index, draw).unwrap(); >::insert(market_id, draws); >::set_block_number(pre_vote + 1u64.saturated_into::()); @@ -319,12 +328,14 @@ benchmarks! { let mut draws = >::get(market_id); let draws_len = draws.len(); - draws[draws_len.saturating_sub(1usize)] = Draw { + draws.remove(0); + let index = draws.binary_search_by_key(&caller, |draw| draw.juror.clone()).unwrap_or_else(|j| j); + draws.try_insert(index, Draw { juror: caller.clone(), vote: Vote::Secret { commitment }, weight: 1u32, slashable: T::MinJurorStake::get(), - }; + }).unwrap(); >::insert(market_id, draws); >::set_block_number(vote_end + 1u64.saturated_into::()); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bd6ad60a5..db961a1d1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1425,10 +1425,6 @@ mod pallet { ensure!(jurors_len >= necessary_jurors_weight, Error::::NotEnoughJurors); let random_jurors = Self::choose_multiple_weighted(necessary_jurors_weight)?; - debug_assert!( - random_jurors.windows(2).all(|window| window[0].juror <= window[1].juror), - "The vector is not sorted by the juror field" - ); debug_assert!( random_jurors.len() <= T::MaxSelectedDraws::get() as usize, @@ -1436,6 +1432,15 @@ mod pallet { `MaxSelectedDraws`." ); + debug_assert!({ + // proove that random jurors is sorted by juror account id + // this is helpful to use binary search later on + let prev = random_jurors.clone(); + let mut sorted = random_jurors.clone(); + sorted.sort_by_key(|draw| draw.juror.clone()); + prev.len() == sorted.len() && prev.iter().zip(sorted.iter()).all(|(a, b)| a == b) + }); + // TODO what is the maximum number of draws with delegations? It is using necessary_jurors_weight (the number of atoms / draw weight) for the last round // new appeal round should have a fresh set of draws Ok(>::truncate_from(random_jurors)) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index ee8aaadd2..5936d718e 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -37,7 +37,10 @@ use frame_support::{ }; use pallet_balances::{BalanceLock, NegativeImbalance}; use rand::seq::SliceRandom; -use sp_runtime::traits::{BlakeTwo256, Hash, Zero}; +use sp_runtime::{ + traits::{BlakeTwo256, Hash, Zero}, + Perquintill, +}; use test_case::test_case; use zeitgeist_primitives::{ constants::{ @@ -649,7 +652,8 @@ fn vote_works() { let mut draws = >::get(market_id); assert_eq!(draws.len(), 5usize); let slashable = MinJurorStake::get(); - let alice_index = 3usize; + let alice_index = + draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(market_id, draws); >::insert( @@ -834,7 +838,8 @@ fn reveal_vote_works() { let mut draws = >::get(market_id); assert_eq!(draws.len(), 5usize); let slashable = MinJurorStake::get(); - let alice_index = 3usize; + let alice_index = + draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(market_id, draws); >::insert( @@ -1857,30 +1862,35 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let wrong_outcome_0 = OutcomeReport::Scalar(69u128); let wrong_outcome_1 = OutcomeReport::Scalar(56u128); + let alice_slashable = MinJurorStake::get(); + let bob_slashable = 2 * MinJurorStake::get(); + let charlie_slashable = 3 * MinJurorStake::get(); + let dave_slashable = 4 * MinJurorStake::get(); + let draws: crate::SelectedDrawsOf = vec![ Draw { juror: ALICE, weight: 1, vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, - slashable: MinJurorStake::get(), + slashable: alice_slashable, }, Draw { juror: BOB, weight: 1, vote: Vote::Revealed { commitment, outcome: wrong_outcome_0, salt }, - slashable: 2 * MinJurorStake::get(), + slashable: bob_slashable, }, Draw { juror: CHARLIE, weight: 1, vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, - slashable: 3 * MinJurorStake::get(), + slashable: charlie_slashable, }, Draw { juror: DAVE, weight: 1, vote: Vote::Revealed { commitment, outcome: wrong_outcome_1, salt }, - slashable: 4 * MinJurorStake::get(), + slashable: dave_slashable, }, ] .try_into() @@ -1912,14 +1922,19 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let bob_slashed = last_draws[BOB as usize].slashable; let dave_slashed = last_draws[DAVE as usize].slashable; let slashed = bob_slashed + dave_slashed + tardy_or_denounced_value; + + let winners_risked_amount = charlie_slashable + alice_slashable; + + let alice_share = Perquintill::from_rational(alice_slashable, winners_risked_amount); let free_alice_after = Balances::free_balance(ALICE); - assert_eq!(free_alice_after, free_alice_before + slashed / 2); + assert_eq!(free_alice_after, free_alice_before + alice_share * slashed); let free_bob_after = Balances::free_balance(BOB); assert_eq!(free_bob_after, free_bob_before - bob_slashed); + let charlie_share = Perquintill::from_rational(charlie_slashable, winners_risked_amount); let free_charlie_after = Balances::free_balance(CHARLIE); - assert_eq!(free_charlie_after, free_charlie_before + slashed / 2); + assert_eq!(free_charlie_after, free_charlie_before + charlie_share * slashed); let free_dave_after = Balances::free_balance(DAVE); assert_eq!(free_dave_after, free_dave_before - dave_slashed); diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index e8d3da7cf..e2e0bcbda 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -32,7 +32,7 @@ use frame_support::{ dispatch::{DispatchError, DispatchResultWithPostInfo}, traits::{NamedReservableCurrency, OnInitialize}, }; -use sp_runtime::traits::BlakeTwo256; +use sp_runtime::{traits::BlakeTwo256, Perquintill}; use test_case::test_case; use zrml_court::{types::*, Error as CError}; @@ -2961,13 +2961,6 @@ fn it_resolves_a_disputed_court_market() { assert_ok!(Court::join_court(Origin::signed(*j), amount)); } - let free_juror_0_before = Balances::free_balance(&juror_0); - let free_juror_1_before = Balances::free_balance(&juror_1); - let free_juror_2_before = Balances::free_balance(&juror_2); - let free_juror_3_before = Balances::free_balance(&juror_3); - let free_juror_4_before = Balances::free_balance(&juror_4); - let free_juror_5_before = Balances::free_balance(&juror_5); - let end = 2; assert_ok!(PredictionMarkets::create_market( Origin::signed(ALICE), @@ -3005,14 +2998,14 @@ fn it_resolves_a_disputed_court_market() { zrml_court::SelectedDraws::::remove(market_id); let mut draws = zrml_court::SelectedDraws::::get(market_id); for juror in &[juror_0, juror_1, juror_2, juror_3, juror_4, juror_5] { - draws - .try_push(Draw { - juror: *juror, - weight: 1, - vote: Vote::Drawn, - slashable: MinJurorStake::get(), - }) - .unwrap(); + let draw = Draw { + juror: *juror, + weight: 1, + vote: Vote::Drawn, + slashable: MinJurorStake::get(), + }; + let index = draws.binary_search_by_key(juror, |draw| draw.juror).unwrap_or_else(|j| j); + draws.try_insert(index, draw).unwrap(); } let old_draws = draws.clone(); zrml_court::SelectedDraws::::insert(market_id, draws); @@ -3088,31 +3081,53 @@ fn it_resolves_a_disputed_court_market() { let court_after = zrml_court::Courts::::get(market_id).unwrap(); assert_eq!(court_after.status, CourtStatus::Closed { winner: outcome_0 }); + let free_juror_0_before = Balances::free_balance(&juror_0); + let free_juror_1_before = Balances::free_balance(&juror_1); + let free_juror_2_before = Balances::free_balance(&juror_2); + let free_juror_3_before = Balances::free_balance(&juror_3); + let free_juror_4_before = Balances::free_balance(&juror_4); + let free_juror_5_before = Balances::free_balance(&juror_5); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(juror_0), market_id)); let free_juror_0_after = Balances::free_balance(&juror_0); + let slashable_juror_0 = + old_draws.iter().find(|draw| draw.juror == juror_0).unwrap().slashable; let free_juror_1_after = Balances::free_balance(&juror_1); + let slashable_juror_1 = + old_draws.iter().find(|draw| draw.juror == juror_1).unwrap().slashable; let free_juror_2_after = Balances::free_balance(&juror_2); + let slashable_juror_2 = + old_draws.iter().find(|draw| draw.juror == juror_2).unwrap().slashable; let free_juror_3_after = Balances::free_balance(&juror_3); + let slashable_juror_3 = + old_draws.iter().find(|draw| draw.juror == juror_3).unwrap().slashable; let free_juror_4_after = Balances::free_balance(&juror_4); + let slashable_juror_4 = + old_draws.iter().find(|draw| draw.juror == juror_4).unwrap().slashable; let free_juror_5_after = Balances::free_balance(&juror_5); + let slashable_juror_5 = + old_draws.iter().find(|draw| draw.juror == juror_5).unwrap().slashable; let mut total_slashed = 0; // juror_1 voted for the wrong outcome => slashed - assert_eq!(free_juror_1_after, free_juror_1_before - old_draws[1].slashable); - total_slashed += old_draws[1].slashable; + assert_eq!(free_juror_1_before - free_juror_1_after, slashable_juror_1); + total_slashed += slashable_juror_1; // juror_3 was denounced by juror_0 => slashed - assert_eq!(free_juror_3_after, free_juror_3_before - old_draws[3].slashable); - total_slashed += old_draws[3].slashable; + assert_eq!(free_juror_3_before - free_juror_3_after, slashable_juror_3); + total_slashed += slashable_juror_3; // juror_4 failed to vote => slashed - assert_eq!(free_juror_4_after, free_juror_4_before - old_draws[4].slashable); - total_slashed += old_draws[4].slashable; + assert_eq!(free_juror_4_before - free_juror_4_after, slashable_juror_4); + total_slashed += slashable_juror_4; // juror_5 failed to reveal => slashed - assert_eq!(free_juror_5_after, free_juror_5_before - old_draws[5].slashable); - total_slashed += old_draws[5].slashable; + assert_eq!(free_juror_5_before - free_juror_5_after, slashable_juror_5); + total_slashed += slashable_juror_5; // juror_0 and juror_2 voted for the right outcome => rewarded - assert_eq!(free_juror_0_after, free_juror_0_before + total_slashed / 2); - assert_eq!(free_juror_2_after, free_juror_2_before + total_slashed / 2); + let total_winner_stake = slashable_juror_0 + slashable_juror_2; + let juror_0_share = Perquintill::from_rational(slashable_juror_0, total_winner_stake); + assert_eq!(free_juror_0_after, free_juror_0_before + juror_0_share * total_slashed); + let juror_2_share = Perquintill::from_rational(slashable_juror_2, total_winner_stake); + assert_eq!(free_juror_2_after, free_juror_2_before + juror_2_share * total_slashed); }; ExtBuilder::default().build().execute_with(|| { test(Asset::Ztg); From a9f5f4678dc1e8da5888ccdeacdb083a64f6f6af Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 19 Apr 2023 13:54:30 +0200 Subject: [PATCH 260/368] improve benchmarking --- zrml/court/src/benchmarks.rs | 29 ++++++++++++++++++++++++++++- zrml/court/src/lib.rs | 10 ++++++---- zrml/court/src/weights.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 31f97fd92..5cf5f77cc 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -28,7 +28,7 @@ use crate::{ AppealInfo, BalanceOf, Call, Config, Courts, JurorPool, Jurors, MarketOf, Pallet as Court, Pallet, RequestBlock, SelectedDraws, }; -use alloc::vec; +use alloc::{vec, vec::Vec}; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; @@ -189,6 +189,26 @@ benchmarks! { .saturating_add(1u128.saturated_into::>()); }: _(RawOrigin::Signed(caller), new_stake) + delegate { + // start with higher value to allow delegations on existing jurors + let j in 10..(T::MaxJurors::get() - 1); + let d in 1..T::MaxDelegations::get(); + + fill_pool::(j)?; + + let caller: T::AccountId = whitelisted_caller(); + join_with_min_stake::(&caller)?; + + let juror_pool = >::get(); + let mut delegations = Vec::::new(); + juror_pool.iter() + .filter(|pool_item| pool_item.juror != caller).take(d as usize) + .for_each(|pool_item| delegations.push(pool_item.juror.clone())); + + let new_stake = T::MinJurorStake::get() + .saturating_add(1u128.saturated_into::>()); + }: _(RawOrigin::Signed(caller), new_stake, delegations) + prepare_exit_court { let j in 0..(T::MaxJurors::get() - 1); @@ -473,6 +493,13 @@ benchmarks! { Court::::handle_inflation(now); } + select_jurors { + let a in 0..T::MaxAppeals::get(); + fill_pool::(T::MaxJurors::get())?; + }: { + let _ = Court::::select_jurors(a as usize).unwrap(); + } + impl_benchmark_test_suite!( Court, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index db961a1d1..efafaef1c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -463,8 +463,10 @@ mod pallet { Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) } - // TODO benchmark - #[pallet::weight(T::WeightInfo::join_court(T::MaxJurors::get()))] + #[pallet::weight({ + let delegations_len = delegations.len() as u32; + T::WeightInfo::delegate(T::MaxJurors::get(), delegations_len) + })] #[transactional] pub fn delegate( origin: OriginFor, @@ -474,6 +476,7 @@ mod pallet { let who = ensure_signed(origin)?; ensure!(!delegations.is_empty(), Error::::NoDelegations); + let delegations_len = delegations.len() as u32; let mut sorted_delegations: DelegationsOf = delegations.clone().try_into().map_err(|_| Error::::MaxDelegationsReached)?; @@ -503,7 +506,7 @@ mod pallet { delegated_jurors: delegations, }); - Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) + Ok(Some(T::WeightInfo::delegate(jurors_len, delegations_len)).into()) } /// Prepare as a juror to exit the court. @@ -1409,7 +1412,6 @@ mod pallet { } } - // TODO benchmark select_jurors explicitly for future comparisons // Selects the jurors for the next round. // The `consumed_stake` in `JurorPool` and `active_lock` in `Jurors` is increased // equally according to the weight inside the `new_draws`. diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index cabc397ec..014996a2e 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -46,6 +46,7 @@ use frame_support::{traits::Get, weights::Weight}; /// zrml_court (automatically generated) pub trait WeightInfoZeitgeist { fn join_court(j: u32) -> Weight; + fn delegate(j: u32, d: u32) -> Weight; fn prepare_exit_court(j: u32) -> Weight; fn exit_court_remove() -> Weight; fn exit_court_set() -> Weight; @@ -56,6 +57,7 @@ pub trait WeightInfoZeitgeist { fn reassign_juror_stakes(d: u32) -> Weight; fn set_inflation() -> Weight; fn handle_inflation(j: u32) -> Weight; + fn select_jurors(a: u32) -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -71,6 +73,19 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } + // Storage: Court JurorPool (r:1 w:1) + // Storage: Court Jurors (r:6 w:1) + // Storage: Balances Locks (r:1 w:1) + fn delegate(j: u32, d: u32) -> Weight { + Weight::from_ref_time(38_291_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(133_000).saturating_mul(j.into())) + // Standard Error: 70_000 + .saturating_add(Weight::from_ref_time(2_632_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(3)) + } // Storage: Court Jurors (r:1 w:0) // Storage: Court JurorPool (r:1 w:1) fn prepare_exit_court(j: u32) -> Weight { @@ -175,4 +190,16 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) } + // Storage: Court JurorPool (r:1 w:1) + // Storage: Court JurorsSelectionNonce (r:1 w:1) + // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + // Storage: Court Jurors (r:5 w:5) + fn select_jurors(a: u32) -> Weight { + Weight::from_ref_time(87_684_000) + // Standard Error: 1_545_000 + .saturating_add(Weight::from_ref_time(240_196_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads((18_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes((18_u64).saturating_mul(a.into()))) + } } From 4a9a04bb6b03e969164732d6904dd93c92c3554d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 19 Apr 2023 14:01:27 +0200 Subject: [PATCH 261/368] do small change --- zrml/court/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index efafaef1c..d46432490 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -463,10 +463,7 @@ mod pallet { Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) } - #[pallet::weight({ - let delegations_len = delegations.len() as u32; - T::WeightInfo::delegate(T::MaxJurors::get(), delegations_len) - })] + #[pallet::weight(T::WeightInfo::delegate(T::MaxJurors::get(), delegations.len() as u32))] #[transactional] pub fn delegate( origin: OriginFor, From 86bd97e43f63a7b43891c3fd984ed90f3421cb43 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 20 Apr 2023 07:31:37 +0200 Subject: [PATCH 262/368] little benchmark fix --- zrml/authorized/src/benchmarks.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index ff3cbd3f1..714fcdf3c 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -110,12 +110,7 @@ benchmarks! { let market = market_mock::(); T::MarketCommons::push_market(market.clone()).unwrap(); - frame_system::Pallet::::set_block_number(42u32.into()); - - let now = frame_system::Pallet::::block_number(); - let resolve_at = now.saturating_add(T::CorrectionPeriod::get()); - - let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(0) }; + let report = AuthorityReport { resolve_at: 0u32.into(), outcome: OutcomeReport::Scalar(0) }; AuthorizedOutcomeReports::::insert(market_id, report); }: { Authorized::::on_resolution(&market_id, &market).unwrap(); From af60129ced95718e1a2d136f8b0705e089f5da42 Mon Sep 17 00:00:00 2001 From: Chralt Date: Thu, 20 Apr 2023 07:32:38 +0200 Subject: [PATCH 263/368] Update zrml/authorized/src/benchmarks.rs Co-authored-by: Harald Heckmann --- zrml/authorized/src/benchmarks.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index 714fcdf3c..74de5613c 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -131,11 +131,7 @@ benchmarks! { let market_id = 0u32.into(); let market = market_mock::(); T::MarketCommons::push_market(market.clone()).unwrap(); - - let now = frame_system::Pallet::::block_number(); - let resolve_at = now.saturating_add(T::CorrectionPeriod::get()); - - let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(0) }; + let report = AuthorityReport { resolve_at: 0u32.into(), outcome: OutcomeReport::Scalar(0) }; AuthorizedOutcomeReports::::insert(market_id, report); }: { Authorized::::get_auto_resolve(&market_id, &market).unwrap(); From ddf14ffc9006d715b26a167f7e5e4f1dd8ffb693 Mon Sep 17 00:00:00 2001 From: Chralt Date: Thu, 20 Apr 2023 07:33:42 +0200 Subject: [PATCH 264/368] Update zrml/authorized/src/benchmarks.rs Co-authored-by: Harald Heckmann --- zrml/authorized/src/benchmarks.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index 74de5613c..1ddf58cf8 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -157,11 +157,7 @@ benchmarks! { let market_id = 0u32.into(); let market = market_mock::(); T::MarketCommons::push_market(market.clone()).unwrap(); - - let now = frame_system::Pallet::::block_number(); - let resolve_at = now.saturating_add(T::CorrectionPeriod::get()); - - let report = AuthorityReport { resolve_at, outcome: OutcomeReport::Scalar(0) }; + let report = AuthorityReport { resolve_at: 0u32.into(), outcome: OutcomeReport::Scalar(0) }; AuthorizedOutcomeReports::::insert(market_id, report); }: { Authorized::::clear(&market_id, &market).unwrap(); From 85131915f06664f8c2d7857e8e6809760fca86a7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 20 Apr 2023 07:58:11 +0200 Subject: [PATCH 265/368] use result with weight struct --- zrml/prediction-markets/src/lib.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 0ae48662b..3169da074 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -66,7 +66,8 @@ mod pallet { types::{ Asset, Bond, Deadlines, MDMWeight, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, MultiHash, - OldMarketDispute, OutcomeReport, Report, ScalarPosition, ScoringRule, SubsidyUntil, + OldMarketDispute, OutcomeReport, Report, ResultWithWeightInfo, ScalarPosition, + ScoringRule, SubsidyUntil, }, }; #[cfg(feature = "with-global-disputes")] @@ -2555,13 +2556,14 @@ mod pallet { fn resolve_disputed_market( market_id: &MarketIdOf, market: &MarketOf, - ) -> Result<(OutcomeReport, Weight), DispatchError> { + ) -> Result, DispatchError> { let report = market.report.as_ref().ok_or(Error::::MarketIsNotReported)?; let mut weight = Weight::zero(); - let (resolved_outcome, w): (OutcomeReport, Weight) = + let res: ResultWithWeightInfo = Self::get_resolved_outcome(market_id, market, &report.outcome)?; - weight = weight.saturating_add(w); + let resolved_outcome = res.result; + weight = weight.saturating_add(res.weight); let imbalance_left = Self::settle_bonds(market_id, market, &resolved_outcome, report)?; @@ -2599,7 +2601,9 @@ mod pallet { T::Slash::on_unbalanced(remainder); - Ok((resolved_outcome, weight)) + let res = ResultWithWeightInfo { result: resolved_outcome, weight }; + + Ok(res) } /// Get the outcome the market should resolve to. @@ -2607,7 +2611,7 @@ mod pallet { market_id: &MarketIdOf, market: &MarketOf, reported_outcome: &OutcomeReport, - ) -> Result<(OutcomeReport, Weight), DispatchError> { + ) -> Result, DispatchError> { let mut resolved_outcome_option = None; let mut weight = Weight::zero(); @@ -2646,7 +2650,9 @@ mod pallet { let resolved_outcome = resolved_outcome_option.unwrap_or_else(|| reported_outcome.clone()); - Ok((resolved_outcome, weight)) + let res = ResultWithWeightInfo { result: resolved_outcome, weight }; + + Ok(res) } /// Manage the outstanding bonds (oracle, outsider, dispute) of the market. @@ -2727,10 +2733,9 @@ mod pallet { let resolved_outcome = match market.status { MarketStatus::Reported => Self::resolve_reported_market(market_id, market)?, MarketStatus::Disputed => { - let (resolved_outcome, weight) = - Self::resolve_disputed_market(market_id, market)?; - total_weight = total_weight.saturating_add(weight); - resolved_outcome + let res = Self::resolve_disputed_market(market_id, market)?; + total_weight = total_weight.saturating_add(res.weight); + res.result } _ => return Err(Error::::InvalidMarketStatus.into()), }; From 797917ca8ef45e41a9bef089cae9271cfcbb8c1a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 20 Apr 2023 13:20:03 +0200 Subject: [PATCH 266/368] improve code --- primitives/src/constants/mock.rs | 2 +- runtime/battery-station/src/lib.rs | 26 ++++++++-- runtime/battery-station/src/parameters.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/court/src/benchmarks.rs | 60 ++++++++++++++++++++--- zrml/court/src/lib.rs | 12 ++++- zrml/prediction-markets/src/lib.rs | 5 +- 7 files changed, 88 insertions(+), 21 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 2792ee922..e907c2158 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -45,7 +45,7 @@ parameter_types! { pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const MaxAppeals: u32 = 4; pub const MaxDelegations: u32 = 5; - pub const MaxSelectedDraws: u32 = 47; + pub const MaxSelectedDraws: u32 = 94; pub const MaxJurors: u32 = 1_000; pub const MinJurorStake: Balance = 50 * CENT; pub const InflationPeriod: BlockNumber = 20; diff --git a/runtime/battery-station/src/lib.rs b/runtime/battery-station/src/lib.rs index c9c005344..fa69db661 100644 --- a/runtime/battery-station/src/lib.rs +++ b/runtime/battery-station/src/lib.rs @@ -104,17 +104,33 @@ pub struct IsCallable; // Currently disables Rikiddo. impl Contains for IsCallable { fn contains(call: &Call) -> bool { - use zeitgeist_primitives::types::ScoringRule::RikiddoSigmoidFeeMarketEma; - use zrml_prediction_markets::Call::{create_market, edit_market}; + use zeitgeist_primitives::types::{ + MarketDisputeMechanism::SimpleDisputes, ScoringRule::RikiddoSigmoidFeeMarketEma, + }; + use zrml_prediction_markets::Call::{ + create_cpmm_market_and_deploy_assets, create_market, edit_market, + }; #[allow(clippy::match_like_matches_macro)] match call { Call::LiquidityMining(_) => false, Call::PredictionMarkets(inner_call) => { match inner_call { - // Disable Rikiddo markets - create_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, - edit_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, + // Disable Rikiddo and SimpleDisputes markets + create_market { + scoring_rule: RikiddoSigmoidFeeMarketEma, + dispute_mechanism: SimpleDisputes, + .. + } => false, + edit_market { + scoring_rule: RikiddoSigmoidFeeMarketEma, + dispute_mechanism: SimpleDisputes, + .. + } => false, + create_cpmm_market_and_deploy_assets { + dispute_mechanism: SimpleDisputes, + .. + } => false, _ => true, } } diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index ec13a50cc..162ff2fb7 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -100,7 +100,7 @@ parameter_types! { /// The maximum number of delegations per juror account. pub const MaxDelegations: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxSelectedDraws: u32 = 47; + pub const MaxSelectedDraws: u32 = 94; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 5bed20760..ce916846f 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -100,7 +100,7 @@ parameter_types! { /// The maximum number of delegations per juror account. pub const MaxDelegations: u32 = 5; /// The maximum number of randomly selected jurors for a dispute. - pub const MaxSelectedDraws: u32 = 47; + pub const MaxSelectedDraws: u32 = 94; /// The maximum number of jurors that can be registered. pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 5cf5f77cc..2b224c3fe 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -25,8 +25,8 @@ extern crate alloc; use crate::{ types::{CourtStatus, Draw, JurorInfo, JurorPoolItem, Vote}, - AppealInfo, BalanceOf, Call, Config, Courts, JurorPool, Jurors, MarketOf, Pallet as Court, - Pallet, RequestBlock, SelectedDraws, + AppealInfo, BalanceOf, Call, Config, Courts, DelegatedStakesOf, JurorPool, Jurors, MarketOf, + Pallet as Court, Pallet, RequestBlock, SelectedDraws, }; use alloc::{vec, vec::Vec}; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; @@ -121,6 +121,28 @@ where Ok(()) } +// assume always worst case for delegations (MaxDelegations), +// because delegations are individual to each juror +fn fill_delegations() +where + T: Config, +{ + let jurors = >::get(); + debug_assert!(jurors.len() >= T::MaxDelegations::get() as usize); + let mut jurors_iter = jurors.iter(); + let mut delegated_jurors = vec![]; + for _ in 0..T::MaxDelegations::get() { + let delegated_juror = jurors_iter.next().unwrap().juror.clone(); + delegated_jurors.push(delegated_juror); + } + for pool_item in jurors_iter { + let juror = &pool_item.juror; + let mut j = >::get(juror).unwrap(); + j.delegations = Some(delegated_jurors.clone().try_into().unwrap()); + >::insert(juror, j); + } +} + fn join_with_min_stake(caller: &T::AccountId) -> Result<(), &'static str> where T: Config, @@ -371,6 +393,7 @@ benchmarks! { let necessary_jurors_weight = Court::::necessary_jurors_weight((T::MaxAppeals::get() - 1) as usize); debug_assert!(necessary_jurors_weight == 47usize); fill_pool::(j)?; + fill_delegations::(); let caller: T::AccountId = whitelisted_caller(); deposit::(&caller); @@ -440,8 +463,10 @@ benchmarks! { } reassign_juror_stakes { - let d in 1..T::MaxSelectedDraws::get(); + // start with 5 jurors to fill the max number of delegations + let d in 5..T::MaxSelectedDraws::get(); + // just to initialize the court let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); fill_pool::(necessary_jurors_weight as u32)?; @@ -450,6 +475,7 @@ benchmarks! { let mut court = >::get(market_id).unwrap(); let winner_outcome = OutcomeReport::Scalar(0u128); + let wrong_outcome = OutcomeReport::Scalar(1u128); court.status = CourtStatus::Closed { winner: winner_outcome.clone() }; >::insert(market_id, court); @@ -457,23 +483,39 @@ benchmarks! { // remove last random selections of on_dispute >::remove(market_id); let mut draws = >::get(market_id); + let mut delegated_stakes: DelegatedStakesOf = Default::default(); for i in 0..d { let juror: T::AccountId = account("juror", i, 0); + deposit::(&juror); >::insert(&juror, JurorInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, delegations: Default::default(), }); - let outcome = winner_outcome.clone(); - let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); - let draw = + let draw = if i < 5 { + delegated_stakes.try_push((juror.clone(), T::MinJurorStake::get())).unwrap(); + + let outcome = if i % 2 == 0 { + wrong_outcome.clone() + } else { + winner_outcome.clone() + }; + let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); Draw { juror, vote: Vote::Revealed { commitment, outcome, salt }, weight: 1u32, slashable: T::MinJurorStake::get(), - }; + } + } else { + Draw { + juror, + vote: Vote::Delegated { delegated_stakes: delegated_stakes.clone() }, + weight: 1u32, + slashable: T::MinJurorStake::get(), + } + }; draws.try_push(draw).unwrap(); } >::insert(market_id, draws); @@ -494,8 +536,10 @@ benchmarks! { } select_jurors { - let a in 0..T::MaxAppeals::get(); + let a in 0..(T::MaxAppeals::get() - 1); fill_pool::(T::MaxJurors::get())?; + + fill_delegations::(); }: { let _ = Court::::select_jurors(a as usize).unwrap(); } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d46432490..d4348448f 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -684,6 +684,7 @@ mod pallet { origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, juror: AccountIdLookupOf, + // TODO: use generic item for votes => court should be a decentralized decision machine outcome: OutcomeReport, salt: T::Hash, ) -> DispatchResultWithPostInfo { @@ -756,6 +757,7 @@ mod pallet { pub fn reveal_vote( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, + // TODO: use generic item for votes => court should be a decentralized decision machine outcome: OutcomeReport, salt: T::Hash, ) -> DispatchResultWithPostInfo { @@ -1425,6 +1427,11 @@ mod pallet { let random_jurors = Self::choose_multiple_weighted(necessary_jurors_weight)?; + // keep in mind that the number of draws is at maximum necessary_jurors_weight * 2 + // because with delegations each juror draw weight + // could delegate an additional juror in addition to the delegator itself + debug_assert!(random_jurors.len() <= 2 * necessary_jurors_weight as usize); + // ensure that we don't truncate some of the selections debug_assert!( random_jurors.len() <= T::MaxSelectedDraws::get() as usize, "The number of randomly selected jurors should be less than or equal to \ @@ -1440,7 +1447,10 @@ mod pallet { prev.len() == sorted.len() && prev.iter().zip(sorted.iter()).all(|(a, b)| a == b) }); - // TODO what is the maximum number of draws with delegations? It is using necessary_jurors_weight (the number of atoms / draw weight) for the last round + // what is the maximum number of draws with delegations? + // It is using necessary_jurors_weight (the number of atoms / draw weight) + // for the last round times two because each delegator + // could potentially add one juror account to the selections // new appeal round should have a fresh set of draws Ok(>::truncate_from(random_jurors)) } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 80370ffeb..ba7a9e69e 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1423,10 +1423,7 @@ mod pallet { ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); ensure!( - matches!( - market.dispute_mechanism, - MarketDisputeMechanism::SimpleDisputes | MarketDisputeMechanism::Court - ), + matches!(market.dispute_mechanism, MarketDisputeMechanism::Court), Error::::InvalidDisputeMechanism ); From 112f5f68ada3711fca3e5b4bac2eaad157c72d2c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 20 Apr 2023 20:12:52 +0200 Subject: [PATCH 267/368] update benchmark and weights --- zrml/court/src/benchmarks.rs | 11 +-- zrml/court/src/lib.rs | 24 ++++++ zrml/court/src/weights.rs | 154 ++++++++++++++++++----------------- 3 files changed, 109 insertions(+), 80 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 2b224c3fe..71d46e1aa 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -212,8 +212,9 @@ benchmarks! { }: _(RawOrigin::Signed(caller), new_stake) delegate { - // start with higher value to allow delegations on existing jurors - let j in 10..(T::MaxJurors::get() - 1); + // jurors greater or equal to MaxDelegations, + // because we can not delegate to a non-existent juror + let j in T::MaxDelegations::get()..(T::MaxJurors::get() - 1); let d in 1..T::MaxDelegations::get(); fill_pool::(j)?; @@ -463,8 +464,8 @@ benchmarks! { } reassign_juror_stakes { - // start with 5 jurors to fill the max number of delegations - let d in 5..T::MaxSelectedDraws::get(); + let d in T::MaxDelegations::get()..T::MaxSelectedDraws::get(); + debug_assert!(T::MaxDelegations::get() < T::MaxSelectedDraws::get()); // just to initialize the court let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); @@ -493,7 +494,7 @@ benchmarks! { prepare_exit_at: None, delegations: Default::default(), }); - let draw = if i < 5 { + let draw = if i < T::MaxDelegations::get() { delegated_stakes.try_push((juror.clone(), T::MinJurorStake::get())).unwrap(); let outcome = if i % 2 == 0 { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index d4348448f..ab302628c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -241,6 +241,9 @@ mod pallet { #[pallet::storage] pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; + // TODO make everything independent from the concept of market ids. + // TODO so that the integration of this pallet elsewhere is easier + /// The randomly selected jurors with their vote. #[pallet::storage] pub type SelectedDraws = @@ -406,11 +409,17 @@ mod pallet { WaitFullInflationPeriod, /// The `prepare_exit_at` field is not present. PrepareExitAtNotPresent, + /// The maximum number of delegations is reached for this account. MaxDelegationsReached, + /// The juror decided to be a delegator. JurorDelegated, + /// A delegation to the own account is not possible. SelfDelegationNotAllowed, + /// The set of delegations has to be distinct. IdenticalDelegationsNotAllowed, + /// The call to `delegate` is not valid if no delegations are provided. NoDelegations, + /// The set of delegations should contain only valid and active juror accounts. DelegatedToInvalidJuror, } @@ -463,6 +472,20 @@ mod pallet { Ok(Some(T::WeightInfo::join_court(jurors_len)).into()) } + /// Join the court to become a delegator. + /// The `amount` of this call represents the total stake of the delegator. + /// If the one of the delegated jurors is selected for a court case, + /// the caller delegates the vote power to the drawn delegated juror. + /// The delegator gets slashed or rewarded according to the delegated jurors decisions. + /// + /// # Arguments + /// + /// - `amount`: The total stake associated with the joining delegator. + /// - `delegations`: The list of jurors to delegate the vote power to. + /// + /// # Weight + /// + /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. #[pallet::weight(T::WeightInfo::delegate(T::MaxJurors::get(), delegations.len() as u32))] #[transactional] pub fn delegate( @@ -1451,6 +1474,7 @@ mod pallet { // It is using necessary_jurors_weight (the number of atoms / draw weight) // for the last round times two because each delegator // could potentially add one juror account to the selections + // new appeal round should have a fresh set of draws Ok(>::truncate_from(random_jurors)) } diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 014996a2e..5592fdc34 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -15,10 +15,11 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . + //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-30, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-20, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -45,19 +46,19 @@ use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within /// zrml_court (automatically generated) pub trait WeightInfoZeitgeist { - fn join_court(j: u32) -> Weight; - fn delegate(j: u32, d: u32) -> Weight; - fn prepare_exit_court(j: u32) -> Weight; + fn join_court(j: u32, ) -> Weight; + fn delegate(j: u32, d: u32, ) -> Weight; + fn prepare_exit_court(j: u32, ) -> Weight; fn exit_court_remove() -> Weight; fn exit_court_set() -> Weight; - fn vote(d: u32) -> Weight; - fn denounce_vote(d: u32) -> Weight; - fn reveal_vote(d: u32) -> Weight; - fn appeal(d: u32, a: u32, r: u32, f: u32) -> Weight; - fn reassign_juror_stakes(d: u32) -> Weight; + fn vote(d: u32, ) -> Weight; + fn denounce_vote(d: u32, ) -> Weight; + fn reveal_vote(d: u32, ) -> Weight; + fn appeal(j: u32, a: u32, r: u32, f: u32, ) -> Weight; + fn reassign_juror_stakes(d: u32, ) -> Weight; fn set_inflation() -> Weight; - fn handle_inflation(j: u32) -> Weight; - fn select_jurors(a: u32) -> Weight; + fn handle_inflation(j: u32, ) -> Weight; + fn select_jurors(a: u32, ) -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -66,39 +67,39 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn join_court(j: u32) -> Weight { - Weight::from_ref_time(46_917_000) + fn join_court(j: u32, ) -> Weight { + Weight::from_ref_time(37_055_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(56_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(91_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:6 w:1) // Storage: Balances Locks (r:1 w:1) - fn delegate(j: u32, d: u32) -> Weight { - Weight::from_ref_time(38_291_000) + fn delegate(j: u32, d: u32, ) -> Weight { + Weight::from_ref_time(47_313_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(133_000).saturating_mul(j.into())) - // Standard Error: 70_000 - .saturating_add(Weight::from_ref_time(2_632_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(122_000).saturating_mul(j.into())) + // Standard Error: 61_000 + .saturating_add(Weight::from_ref_time(1_227_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: Court Jurors (r:1 w:0) + // Storage: Court Jurors (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) - fn prepare_exit_court(j: u32) -> Weight { - Weight::from_ref_time(23_787_000) + fn prepare_exit_court(j: u32, ) -> Weight { + Weight::from_ref_time(22_189_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(49_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(85_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn exit_court_remove() -> Weight { - Weight::from_ref_time(39_000_000) + Weight::from_ref_time(38_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -110,33 +111,34 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Courts (r:1 w:0) - // Storage: Court Draws (r:1 w:1) - fn vote(d: u32) -> Weight { - Weight::from_ref_time(32_226_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(118_000).saturating_mul(d.into())) + // Storage: Court SelectedDraws (r:1 w:1) + fn vote(d: u32, ) -> Weight { + Weight::from_ref_time(49_834_000) + // Standard Error: 3_000 + .saturating_add(Weight::from_ref_time(231_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) - // Storage: Court Draws (r:1 w:1) - // Storage: System Account (r:2 w:2) - fn denounce_vote(d: u32) -> Weight { - Weight::from_ref_time(62_581_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(140_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) + // Storage: Court SelectedDraws (r:1 w:1) + fn denounce_vote(d: u32, ) -> Weight { + Weight::from_ref_time(38_119_000) + // Standard Error: 1_000 + .saturating_add(Weight::from_ref_time(220_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) } + // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) - // Storage: Court Draws (r:1 w:1) - fn reveal_vote(d: u32) -> Weight { - Weight::from_ref_time(44_544_000) - // Standard Error: 13_000 - .saturating_add(Weight::from_ref_time(794_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(3)) + // Storage: Court SelectedDraws (r:1 w:1) + fn reveal_vote(d: u32, ) -> Weight { + Weight::from_ref_time(73_151_000) + // Standard Error: 9_000 + .saturating_add(Weight::from_ref_time(436_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court Courts (r:1 w:1) @@ -145,47 +147,48 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:41 w:41) + // Storage: Court Jurors (r:42 w:41) // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Balances Reserves (r:1 w:1) - fn appeal(j: u32, a: u32, r: u32, _f: u32) -> Weight { + fn appeal(j: u32, a: u32, r: u32, _f: u32, ) -> Weight { Weight::from_ref_time(0) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(661_000).saturating_mul(j.into())) - // Standard Error: 905_000 - .saturating_add(Weight::from_ref_time(332_344_000).saturating_mul(a.into())) - // Standard Error: 36_000 - .saturating_add(Weight::from_ref_time(1_522_000).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(11)) + // Standard Error: 11_000 + .saturating_add(Weight::from_ref_time(1_750_000).saturating_mul(j.into())) + // Standard Error: 4_469_000 + .saturating_add(Weight::from_ref_time(835_228_000).saturating_mul(a.into())) + // Standard Error: 180_000 + .saturating_add(Weight::from_ref_time(9_074_000).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(13)) .saturating_add(T::DbWeight::get().reads((28_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(8)) .saturating_add(T::DbWeight::get().writes((28_u64).saturating_mul(a.into()))) } // Storage: Court Courts (r:1 w:1) - // Storage: Court Draws (r:1 w:1) - // Storage: Court Jurors (r:1 w:1) - // Storage: System Account (r:1 w:0) - fn reassign_juror_stakes(d: u32) -> Weight { - Weight::from_ref_time(37_281_000) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(4_316_000).saturating_mul(d.into())) + // Storage: Court SelectedDraws (r:1 w:1) + // Storage: Court Jurors (r:5 w:5) + // Storage: System Account (r:6 w:5) + fn reassign_juror_stakes(d: u32, ) -> Weight { + Weight::from_ref_time(0) + // Standard Error: 18_000 + .saturating_add(Weight::from_ref_time(66_657_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(d.into()))) } // Storage: Court YearlyInflation (r:0 w:1) fn set_inflation() -> Weight { - Weight::from_ref_time(6_000_000).saturating_add(T::DbWeight::get().writes(1)) + Weight::from_ref_time(10_000_000) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court YearlyInflation (r:1 w:0) // Storage: Court JurorPool (r:1 w:0) // Storage: System Account (r:1 w:1) - fn handle_inflation(j: u32) -> Weight { - Weight::from_ref_time(0) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(13_075_000).saturating_mul(j.into())) + fn handle_inflation(j: u32, ) -> Weight { + Weight::from_ref_time(1_723_972_000) + // Standard Error: 98_000 + .saturating_add(Weight::from_ref_time(14_481_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) @@ -193,13 +196,14 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:5 w:5) - fn select_jurors(a: u32) -> Weight { - Weight::from_ref_time(87_684_000) - // Standard Error: 1_545_000 - .saturating_add(Weight::from_ref_time(240_196_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().reads((18_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes((18_u64).saturating_mul(a.into()))) + // Storage: Court Jurors (r:9 w:5) + fn select_jurors(a: u32, ) -> Weight { + Weight::from_ref_time(208_843_000) + // Standard Error: 1_953_000 + .saturating_add(Weight::from_ref_time(387_998_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().reads((12_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes((12_u64).saturating_mul(a.into()))) } } From 1e0fcbcfb2fb11128327cd66baf69c04c7612dbc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 21 Apr 2023 12:56:21 +0200 Subject: [PATCH 268/368] improve dispute api weight system --- primitives/src/market.rs | 12 -- primitives/src/traits.rs | 2 +- primitives/src/traits/dispute_api.rs | 72 +++---- zrml/authorized/src/authorized_pallet_api.rs | 4 +- zrml/authorized/src/lib.rs | 135 +++++++----- zrml/authorized/src/tests.rs | 11 +- zrml/court/src/court_pallet_api.rs | 4 +- zrml/court/src/lib.rs | 134 ++++++++---- zrml/court/src/tests.rs | 2 +- zrml/prediction-markets/src/lib.rs | 113 +++++----- zrml/simple-disputes/src/lib.rs | 198 ++++++++++-------- .../src/simple_disputes_pallet_api.rs | 4 +- zrml/simple-disputes/src/tests.rs | 2 +- 13 files changed, 382 insertions(+), 311 deletions(-) diff --git a/primitives/src/market.rs b/primitives/src/market.rs index a7a6900d2..6eeb4e9ca 100644 --- a/primitives/src/market.rs +++ b/primitives/src/market.rs @@ -204,18 +204,6 @@ pub struct MarketDispute { pub bond: Balance, } -#[derive(Default)] -pub enum MDMWeight { - Authorized, - Court, - SimpleDisputes { - market_id: MarketId, - market: Market, - }, - #[default] - Default, -} - /// How a market should resolve disputes #[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub enum MarketDisputeMechanism { diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index fe7ec219f..a1be3cd57 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -22,7 +22,7 @@ mod market_id; mod swaps; mod zeitgeist_multi_reservable_currency; -pub use dispute_api::{DisputeApi, DisputeResolutionApi}; +pub use dispute_api::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}; pub use market_commons_pallet_api::MarketCommonsPalletApi; pub use market_id::MarketId; pub use swaps::Swaps; diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index edabaf2f7..385bbd7de 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -20,10 +20,10 @@ extern crate alloc; use crate::{ outcome_report::OutcomeReport, - types::{Asset, GlobalDisputeItem, MDMWeight, Market}, + types::{Asset, GlobalDisputeItem, Market, ResultWithWeightInfo}, }; use alloc::vec::Vec; -use frame_support::{dispatch::DispatchResult, pallet_prelude::Weight}; +use frame_support::pallet_prelude::Weight; use parity_scale_codec::MaxEncodedLen; use sp_runtime::DispatchError; @@ -37,15 +37,6 @@ type MarketOfDisputeApi = Market< Asset<::MarketId>, >; -type MDMWeightOf = MDMWeight< - ::MarketId, - ::AccountId, - ::Balance, - ::BlockNumber, - ::Moment, - Asset<::MarketId>, ->; - type GlobalDisputeItemOfDisputeApi = GlobalDisputeItem<::AccountId, ::Balance>; @@ -62,10 +53,10 @@ pub trait DisputeApi { /// /// Further interaction with the dispute API (if necessary) **should** happen through an /// associated pallet. **May** assume that `market.dispute_mechanism` refers to the calling dispute API. - fn on_dispute(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; - - /// Return the weight of the `on_dispute` function. - fn on_dispute_weight(mdm_info: MDMWeightOf) -> Weight; + fn on_dispute( + market_id: &Self::MarketId, + market: &MarketOfDisputeApi, + ) -> Result, DispatchError>; /// Manage market resolution of a disputed market. /// @@ -79,10 +70,7 @@ pub trait DisputeApi { fn on_resolution( market_id: &Self::MarketId, market: &MarketOfDisputeApi, - ) -> Result, DispatchError>; - - /// Return the weight of the `on_resolution` function. - fn on_resolution_weight(mdm_info: MDMWeightOf) -> Weight; + ) -> Result>, DispatchError>; /// Allow the transfer of funds from the API caller to the API consumer and back. /// This can be based on the final resolution outcome of the market. @@ -101,12 +89,7 @@ pub trait DisputeApi { market: &MarketOfDisputeApi, resolved_outcome: &OutcomeReport, amount: Self::NegativeImbalance, - ) -> Result; - - /// Return the weight of the `exchange` function. - fn exchange_weight(mdm_info: MDMWeightOf) -> Weight; - - /// Query the future resolution block of a disputed market. + ) -> Result, DispatchError>; /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. /// /// # Returns @@ -115,10 +98,7 @@ pub trait DisputeApi { fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOfDisputeApi, - ) -> Result, DispatchError>; - - /// Return the weight of the `get_auto_resolve` function. - fn get_auto_resolve_weight(mdm_info: MDMWeightOf) -> Weight; + ) -> Result>, DispatchError>; /// Returns `true` if the market dispute mechanism /// was unable to come to a conclusion. @@ -126,10 +106,7 @@ pub trait DisputeApi { fn has_failed( market_id: &Self::MarketId, market: &MarketOfDisputeApi, - ) -> Result; - - /// Return the weight of the `has_failed` function. - fn has_failed_weight(mdm_info: MDMWeightOf) -> Weight; + ) -> Result, DispatchError>; /// Called, when a global dispute is started. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. @@ -139,18 +116,33 @@ pub trait DisputeApi { fn on_global_dispute( market_id: &Self::MarketId, market: &MarketOfDisputeApi, - ) -> Result>, DispatchError>; - - /// Return the weight of the `on_global_dispute` function. - fn on_global_dispute_weight(mdm_info: MDMWeightOf) -> Weight; + ) -> Result>>, DispatchError>; /// Allow the API consumer to clear storage items of the dispute mechanism. /// This may be called, when the dispute mechanism is no longer needed. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. - fn clear(market_id: &Self::MarketId, market: &MarketOfDisputeApi) -> DispatchResult; + fn clear( + market_id: &Self::MarketId, + market: &MarketOfDisputeApi, + ) -> Result, DispatchError>; +} - /// Return the weight of the `clear` function. - fn clear_weight(mdm_info: MDMWeightOf) -> Weight; +pub trait DisputeMaxWeightApi { + /// Return the max weight of the `on_dispute` function. + fn on_dispute_max_weight() -> Weight; + /// Return the max weight of the `on_resolution` function. + fn on_resolution_max_weight() -> Weight; + /// Return the max weight of the `exchange` function. + fn exchange_max_weight() -> Weight; + /// Query the future resolution block of a disputed market. + /// Return the max weight of the `get_auto_resolve` function. + fn get_auto_resolve_max_weight() -> Weight; + /// Return the max weight of the `has_failed` function. + fn has_failed_max_weight() -> Weight; + /// Return the max weight of the `on_global_dispute` function. + fn on_global_dispute_max_weight() -> Weight; + /// Return the max weight of the `clear` function. + fn clear_max_weight() -> Weight; } type MarketOfDisputeResolutionApi = Market< diff --git a/zrml/authorized/src/authorized_pallet_api.rs b/zrml/authorized/src/authorized_pallet_api.rs index b9a1f4185..f9ac39c21 100644 --- a/zrml/authorized/src/authorized_pallet_api.rs +++ b/zrml/authorized/src/authorized_pallet_api.rs @@ -15,6 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -use zeitgeist_primitives::traits::DisputeApi; +use zeitgeist_primitives::traits::{DisputeApi, DisputeMaxWeightApi}; -pub trait AuthorizedPalletApi: DisputeApi {} +pub trait AuthorizedPalletApi: DisputeApi + DisputeMaxWeightApi {} diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index ac1929da8..675061058 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -38,7 +38,7 @@ mod pallet { use alloc::vec::Vec; use core::marker::PhantomData; use frame_support::{ - dispatch::{DispatchResult, DispatchResultWithPostInfo}, + dispatch::DispatchResultWithPostInfo, ensure, pallet_prelude::{ConstU32, EnsureOrigin, OptionQuery, StorageMap, Weight}, traits::{Currency, Get, Hooks, IsType, StorageVersion}, @@ -47,10 +47,10 @@ mod pallet { use frame_system::pallet_prelude::OriginFor; use sp_runtime::{traits::Saturating, DispatchError}; use zeitgeist_primitives::{ - traits::{DisputeApi, DisputeResolutionApi}, + traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}, types::{ - Asset, AuthorityReport, GlobalDisputeItem, MDMWeight, Market, MarketDisputeMechanism, - MarketStatus, OutcomeReport, + Asset, AuthorityReport, GlobalDisputeItem, Market, MarketDisputeMechanism, + MarketStatus, OutcomeReport, ResultWithWeightInfo, }, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -75,14 +75,6 @@ mod pallet { MomentOf, Asset>, >; - pub(crate) type MDMWeightOf = MDMWeight< - MarketIdOf, - ::AccountId, - BalanceOf, - ::BlockNumber, - MomentOf, - Asset>, - >; #[pallet::call] impl Pallet { @@ -204,6 +196,39 @@ mod pallet { } } + impl DisputeMaxWeightApi for Pallet + where + T: Config, + { + fn on_dispute_max_weight() -> Weight { + T::WeightInfo::on_dispute_weight() + } + + fn on_resolution_max_weight() -> Weight { + T::WeightInfo::on_resolution_weight() + } + + fn exchange_max_weight() -> Weight { + T::WeightInfo::exchange_weight() + } + + fn get_auto_resolve_max_weight() -> Weight { + T::WeightInfo::get_auto_resolve_weight() + } + + fn has_failed_max_weight() -> Weight { + T::WeightInfo::has_failed_weight() + } + + fn on_global_dispute_max_weight() -> Weight { + T::WeightInfo::on_global_dispute_weight() + } + + fn clear_max_weight() -> Weight { + T::WeightInfo::clear_weight() + } + } + impl DisputeApi for Pallet where T: Config, @@ -216,32 +241,37 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_dispute( + _: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); - Ok(()) - } - fn on_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::on_dispute_weight() + let res = + ResultWithWeightInfo { result: (), weight: T::WeightInfo::on_dispute_weight() }; + + Ok(res) } fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); let report = AuthorizedOutcomeReports::::take(market_id); - Ok(report.map(|r| r.outcome)) - } - fn on_resolution_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::on_resolution_weight() + let res = ResultWithWeightInfo { + result: report.map(|r| r.outcome), + weight: T::WeightInfo::on_resolution_weight(), + }; + + Ok(res) } fn exchange( @@ -249,75 +279,86 @@ mod pallet { market: &MarketOf, _: &OutcomeReport, overall_imbalance: NegativeImbalanceOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); // all funds to treasury - Ok(overall_imbalance) - } + let res = ResultWithWeightInfo { + result: overall_imbalance, + weight: T::WeightInfo::exchange_weight(), + }; - fn exchange_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::exchange_weight() + Ok(res) } fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); - Ok(Self::get_auto_resolve(market_id)) - } - fn get_auto_resolve_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::get_auto_resolve_weight() + let res = ResultWithWeightInfo { + result: Self::get_auto_resolve(market_id), + weight: T::WeightInfo::get_auto_resolve_weight(), + }; + + Ok(res) } - fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { + fn has_failed( + _: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); - Ok(false) - } + let res = + ResultWithWeightInfo { result: false, weight: T::WeightInfo::has_failed_weight() }; - fn has_failed_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::has_failed_weight() + Ok(res) } fn on_global_dispute( _: &Self::MarketId, market: &MarketOf, - ) -> Result>, DispatchError> { + ) -> Result< + ResultWithWeightInfo>>, + DispatchError, + > { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); - Ok(Vec::new()) - } + let res = ResultWithWeightInfo { + result: Vec::new(), + weight: T::WeightInfo::on_global_dispute_weight(), + }; - fn on_global_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::on_global_dispute_weight() + Ok(res) } - fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn clear( + market_id: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Authorized, Error::::MarketDoesNotHaveDisputeMechanismAuthorized ); AuthorizedOutcomeReports::::remove(market_id); - Ok(()) - } - fn clear_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::clear_weight() + let res = ResultWithWeightInfo { result: (), weight: T::WeightInfo::clear_weight() }; + + Ok(res) } } diff --git a/zrml/authorized/src/tests.rs b/zrml/authorized/src/tests.rs index 60cebd57d..515e656e2 100644 --- a/zrml/authorized/src/tests.rs +++ b/zrml/authorized/src/tests.rs @@ -160,7 +160,7 @@ fn authorize_market_outcome_fails_on_unauthorized_account() { #[test] fn on_resolution_fails_if_no_report_was_submitted() { ExtBuilder::default().build().execute_with(|| { - let report = Authorized::on_resolution(&0, &market_mock::()).unwrap(); + let report = Authorized::on_resolution(&0, &market_mock::()).unwrap().result; assert!(report.is_none()); }); } @@ -196,7 +196,10 @@ fn on_resolution_returns_the_reported_outcome() { 0, OutcomeReport::Scalar(2) )); - assert_eq!(Authorized::on_resolution(&0, &market).unwrap(), Some(OutcomeReport::Scalar(2))); + assert_eq!( + Authorized::on_resolution(&0, &market).unwrap().result, + Some(OutcomeReport::Scalar(2)) + ); }); } @@ -241,7 +244,7 @@ fn get_auto_resolve_works() { )); let now = frame_system::Pallet::::block_number(); let resolve_at = now + ::CorrectionPeriod::get(); - assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap(), Some(resolve_at),); + assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap().result, Some(resolve_at),); }); } @@ -249,6 +252,6 @@ fn get_auto_resolve_works() { fn get_auto_resolve_returns_none_without_market_storage() { ExtBuilder::default().build().execute_with(|| { let market = market_mock::(); - assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap(), None,); + assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap().result, None,); }); } diff --git a/zrml/court/src/court_pallet_api.rs b/zrml/court/src/court_pallet_api.rs index 73b9e2829..8d0535aa8 100644 --- a/zrml/court/src/court_pallet_api.rs +++ b/zrml/court/src/court_pallet_api.rs @@ -15,6 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -use zeitgeist_primitives::traits::DisputeApi; +use zeitgeist_primitives::traits::{DisputeApi, DisputeMaxWeightApi}; -pub trait CourtPalletApi: DisputeApi {} +pub trait CourtPalletApi: DisputeApi + DisputeMaxWeightApi {} diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index aca008ad3..7b7f049dc 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -66,10 +66,10 @@ mod pallet { ArithmeticError, DispatchError, SaturatedConversion, }; use zeitgeist_primitives::{ - traits::{DisputeApi, DisputeResolutionApi}, + traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}, types::{ - Asset, GlobalDisputeItem, MDMWeight, Market, MarketDispute, MarketDisputeMechanism, - MarketStatus, OutcomeReport, + Asset, GlobalDisputeItem, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, + OutcomeReport, ResultWithWeightInfo, }, }; use zrml_market_commons::MarketCommonsPalletApi; @@ -102,14 +102,6 @@ mod pallet { MomentOf, Asset>, >; - pub(crate) type MDMWeightOf = MDMWeight< - MarketIdOf, - ::AccountId, - BalanceOf, - ::BlockNumber, - MomentOf, - Asset>, - >; #[pallet::call] impl Pallet { @@ -548,6 +540,39 @@ mod pallet { } } + impl DisputeMaxWeightApi for Pallet + where + T: Config, + { + fn on_dispute_max_weight() -> Weight { + T::WeightInfo::on_dispute_weight() + } + + fn on_resolution_max_weight() -> Weight { + T::WeightInfo::on_resolution_weight() + } + + fn exchange_max_weight() -> Weight { + T::WeightInfo::exchange_weight() + } + + fn get_auto_resolve_max_weight() -> Weight { + T::WeightInfo::get_auto_resolve_weight() + } + + fn has_failed_max_weight() -> Weight { + T::WeightInfo::has_failed_weight() + } + + fn on_global_dispute_max_weight() -> Weight { + T::WeightInfo::on_global_dispute_weight() + } + + fn clear_max_weight() -> Weight { + T::WeightInfo::clear_weight() + } + } + impl DisputeApi for Pallet where T: Config, @@ -560,17 +585,19 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_dispute( + _: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - Ok(()) - } + let res = + ResultWithWeightInfo { result: (), weight: T::WeightInfo::on_dispute_weight() }; - fn on_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::on_dispute_weight() + Ok(res) } // Set jurors that sided on the second most voted outcome as tardy. Jurors are only @@ -580,7 +607,7 @@ mod pallet { fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism @@ -602,11 +629,13 @@ mod pallet { Self::slash_losers_to_award_winners(&valid_winners_and_losers, &first)?; let _ = Votes::::clear_prefix(market_id, u32::max_value(), None); let _ = RequestedJurors::::clear_prefix(market_id, u32::max_value(), None); - Ok(Some(first)) - } - fn on_resolution_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::on_resolution_weight() + let res = ResultWithWeightInfo { + result: Some(first), + weight: T::WeightInfo::on_resolution_weight(), + }; + + Ok(res) } fn exchange( @@ -614,73 +643,86 @@ mod pallet { market: &MarketOf, _: &OutcomeReport, overall_imbalance: NegativeImbalanceOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); // TODO all funds to treasury? - Ok(overall_imbalance) - } - fn exchange_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::exchange_weight() + let res = ResultWithWeightInfo { + result: overall_imbalance, + weight: T::WeightInfo::exchange_weight(), + }; + Ok(res) } fn get_auto_resolve( _: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - Ok(None) - } - fn get_auto_resolve_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::get_auto_resolve_weight() + let res = ResultWithWeightInfo { + result: None, + weight: T::WeightInfo::get_auto_resolve_weight(), + }; + + Ok(res) } - fn has_failed(_: &Self::MarketId, market: &MarketOf) -> Result { + fn has_failed( + _: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - Ok(false) - } - fn has_failed_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::has_failed_weight() + let res = + ResultWithWeightInfo { result: false, weight: T::WeightInfo::has_failed_weight() }; + + Ok(res) } fn on_global_dispute( _: &Self::MarketId, market: &MarketOf, - ) -> Result>, DispatchError> { + ) -> Result< + ResultWithWeightInfo>>, + DispatchError, + > { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); - Ok(Vec::new()) - } - fn on_global_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::on_global_dispute_weight() + let res = ResultWithWeightInfo { + result: Vec::new(), + weight: T::WeightInfo::on_global_dispute_weight(), + }; + + Ok(res) } - fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn clear( + market_id: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::Court, Error::::MarketDoesNotHaveCourtMechanism ); let _ = Votes::::clear_prefix(market_id, u32::max_value(), None); let _ = RequestedJurors::::clear_prefix(market_id, u32::max_value(), None); - Ok(()) - } - fn clear_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::clear_weight() + let res = ResultWithWeightInfo { result: (), weight: T::WeightInfo::clear_weight() }; + + Ok(res) } } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index a7ef0e046..0932af74b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -202,7 +202,7 @@ fn on_resolution_decides_market_outcome_based_on_the_majority() { Court::vote(Origin::signed(ALICE), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(BOB), 0, OutcomeReport::Scalar(1)).unwrap(); Court::vote(Origin::signed(CHARLIE), 0, OutcomeReport::Scalar(2)).unwrap(); - let outcome = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap(); + let outcome = Court::on_resolution(&0, &DEFAULT_MARKET).unwrap().result; assert_eq!(outcome, Some(OutcomeReport::Scalar(1))); }); } diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 3169da074..5ba8036a0 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -64,10 +64,9 @@ mod pallet { constants::MILLISECS_PER_BLOCK, traits::{DisputeApi, DisputeResolutionApi, Swaps, ZeitgeistAssetManager}, types::{ - Asset, Bond, Deadlines, MDMWeight, Market, MarketBonds, MarketCreation, - MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, MultiHash, - OldMarketDispute, OutcomeReport, Report, ResultWithWeightInfo, ScalarPosition, - ScoringRule, SubsidyUntil, + Asset, Bond, Deadlines, Market, MarketBonds, MarketCreation, MarketDisputeMechanism, + MarketPeriod, MarketStatus, MarketType, MultiHash, OldMarketDispute, OutcomeReport, + Report, ResultWithWeightInfo, ScalarPosition, ScoringRule, SubsidyUntil, }, }; #[cfg(feature = "with-global-disputes")] @@ -76,6 +75,7 @@ mod pallet { zrml_global_disputes::GlobalDisputesPalletApi, }; + use zeitgeist_primitives::traits::DisputeMaxWeightApi; use zrml_liquidity_mining::LiquidityMiningPalletApi; use zrml_market_commons::MarketCommonsPalletApi; @@ -619,8 +619,8 @@ mod pallet { /// Complexity: `O(n)`, where `n` is the number of outstanding disputes. #[pallet::weight( T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_weight(MDMWeight::Default).saturating_add( - T::SimpleDisputes::on_dispute_weight(MDMWeight::Default) + T::Court::on_dispute_max_weight().saturating_add( + T::SimpleDisputes::on_dispute_max_weight() ) ) )] @@ -637,26 +637,18 @@ mod pallet { let overweight = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_dispute(&market_id, &market)?; - T::Court::on_dispute_weight(MDMWeight::Court).saturating_add( - T::SimpleDisputes::on_dispute_weight(MDMWeight::SimpleDisputes { - market_id, - market: market.clone(), - }), - ) + T::Court::on_dispute_max_weight() + .saturating_add(T::SimpleDisputes::on_dispute_max_weight()) } MarketDisputeMechanism::Court => { T::Court::on_dispute(&market_id, &market)?; - T::Authorized::on_dispute_weight(MDMWeight::Authorized).saturating_add( - T::SimpleDisputes::on_dispute_weight(MDMWeight::SimpleDisputes { - market_id, - market: market.clone(), - }), - ) + T::Authorized::on_dispute_max_weight() + .saturating_add(T::SimpleDisputes::on_dispute_max_weight()) } MarketDisputeMechanism::SimpleDisputes => { T::SimpleDisputes::on_dispute(&market_id, &market)?; - T::Court::on_dispute_weight(MDMWeight::Court) - .saturating_add(T::Authorized::on_dispute_weight(MDMWeight::Authorized)) + T::Court::on_dispute_max_weight() + .saturating_add(T::Authorized::on_dispute_max_weight()) } }; @@ -673,12 +665,8 @@ mod pallet { Self::deposit_event(Event::MarketDisputed(market_id, MarketStatus::Disputed)); let full_weight = T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_weight(MDMWeight::Court).saturating_add( - T::SimpleDisputes::on_dispute_weight(MDMWeight::SimpleDisputes { - market_id, - market, - }), - ), + T::Court::on_dispute_max_weight() + .saturating_add(T::SimpleDisputes::on_dispute_max_weight()), ); Ok((Some(full_weight.saturating_sub(overweight))).into()) @@ -1473,7 +1461,7 @@ mod pallet { // TODO(#782): use multiple benchmarks paths for different dispute mechanisms - let has_failed = match market.dispute_mechanism { + let res_0 = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::has_failed(&market_id, &market)? } @@ -1482,9 +1470,10 @@ mod pallet { T::SimpleDisputes::has_failed(&market_id, &market)? } }; + let has_failed = res_0.result; ensure!(has_failed, Error::::MarketDisputeMechanismNotFailed); - let gd_items = match market.dispute_mechanism { + let res_1 = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_global_dispute(&market_id, &market)? } @@ -1496,6 +1485,8 @@ mod pallet { } }; + let gd_items = res_1.result; + T::GlobalDisputes::push_voting_outcome( &market_id, report.outcome.clone(), @@ -1701,7 +1692,7 @@ mod pallet { type ResolveOrigin: EnsureOrigin; /// See [`DisputeApi`]. - type SimpleDisputes: DisputeApi< + type SimpleDisputes: zrml_simple_disputes::SimpleDisputesPalletApi< AccountId = Self::AccountId, Balance = BalanceOf, NegativeImbalance = NegativeImbalanceOf, @@ -2238,17 +2229,18 @@ mod pallet { } MarketStatus::Disputed => { // TODO(#782): use multiple benchmarks paths for different dispute mechanisms - let auto_resolve_block_opt = match market.dispute_mechanism { - MarketDisputeMechanism::Authorized => { - T::Authorized::get_auto_resolve(market_id, &market)? - } - MarketDisputeMechanism::Court => { - T::Court::get_auto_resolve(market_id, &market)? - } - MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::get_auto_resolve(market_id, &market)? - } - }; + let ResultWithWeightInfo { result: auto_resolve_block_opt, weight: _ } = + match market.dispute_mechanism { + MarketDisputeMechanism::Authorized => { + T::Authorized::get_auto_resolve(market_id, &market)? + } + MarketDisputeMechanism::Court => { + T::Court::get_auto_resolve(market_id, &market)? + } + MarketDisputeMechanism::SimpleDisputes => { + T::SimpleDisputes::get_auto_resolve(market_id, &market)? + } + }; if let Some(auto_resolve_block) = auto_resolve_block_opt { let ids_len = remove_auto_resolve::(market_id, auto_resolve_block); (ids_len, 0u32) @@ -2569,32 +2561,32 @@ mod pallet { let remainder = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - let remainder = T::Authorized::exchange( + let res = T::Authorized::exchange( market_id, market, &resolved_outcome, imbalance_left, )?; - weight = weight - .saturating_add(T::Authorized::exchange_weight(MDMWeight::Authorized)); + let remainder = res.result; + weight = weight.saturating_add(res.weight); remainder } MarketDisputeMechanism::Court => { - let remainder = + let res = T::Court::exchange(market_id, market, &resolved_outcome, imbalance_left)?; - weight = weight.saturating_add(T::Court::exchange_weight(MDMWeight::Court)); + let remainder = res.result; + weight = weight.saturating_add(res.weight); remainder } MarketDisputeMechanism::SimpleDisputes => { - let remainder = T::SimpleDisputes::exchange( + let res = T::SimpleDisputes::exchange( market_id, market, &resolved_outcome, imbalance_left, )?; - weight = weight.saturating_add(T::SimpleDisputes::exchange_weight( - MDMWeight::SimpleDisputes { market_id: *market_id, market: market.clone() }, - )); + let remainder = res.result; + weight = weight.saturating_add(res.weight); remainder } }; @@ -2625,24 +2617,19 @@ mod pallet { if resolved_outcome_option.is_none() { resolved_outcome_option = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - weight = weight.saturating_add(T::Authorized::on_resolution_weight( - MDMWeight::Authorized, - )); - T::Authorized::on_resolution(market_id, market)? + let res = T::Authorized::on_resolution(market_id, market)?; + weight = weight.saturating_add(res.weight); + res.result } MarketDisputeMechanism::Court => { - weight = - weight.saturating_add(T::Court::on_resolution_weight(MDMWeight::Court)); - T::Court::on_resolution(market_id, market)? + let res = T::Court::on_resolution(market_id, market)?; + weight = weight.saturating_add(res.weight); + res.result } MarketDisputeMechanism::SimpleDisputes => { - weight = weight.saturating_add(T::SimpleDisputes::on_resolution_weight( - MDMWeight::SimpleDisputes { - market_id: *market_id, - market: market.clone(), - }, - )); - T::SimpleDisputes::on_resolution(market_id, market)? + let res = T::SimpleDisputes::on_resolution(market_id, market)?; + weight = weight.saturating_add(res.weight); + res.result } }; } diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index c96ff8280..aa60ff84b 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -31,10 +31,10 @@ pub mod weights; pub use pallet::*; pub use simple_disputes_pallet_api::SimpleDisputesPalletApi; use zeitgeist_primitives::{ - traits::{DisputeApi, DisputeResolutionApi, ZeitgeistAssetManager}, + traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi, ZeitgeistAssetManager}, types::{ - Asset, GlobalDisputeItem, MDMWeight, Market, MarketDispute, MarketDisputeMechanism, - MarketStatus, OutcomeReport, Report, + Asset, GlobalDisputeItem, Market, MarketDispute, MarketDisputeMechanism, MarketStatus, + OutcomeReport, Report, ResultWithWeightInfo, }, }; @@ -124,14 +124,6 @@ mod pallet { MomentOf, Asset>, >; - pub(crate) type MDMWeightOf = MDMWeight< - MarketIdOf, - ::AccountId, - BalanceOf, - ::BlockNumber, - MomentOf, - Asset>, - >; pub(crate) type DisputesOf = BoundedVec< MarketDispute< ::AccountId, @@ -283,6 +275,39 @@ mod pallet { } } + impl DisputeMaxWeightApi for Pallet + where + T: Config, + { + fn on_dispute_max_weight() -> Weight { + T::WeightInfo::on_dispute_weight() + } + + fn on_resolution_max_weight() -> Weight { + T::WeightInfo::on_resolution_weight(T::MaxDisputes::get()) + } + + fn exchange_max_weight() -> Weight { + T::WeightInfo::exchange_weight(T::MaxDisputes::get()) + } + + fn get_auto_resolve_max_weight() -> Weight { + T::WeightInfo::get_auto_resolve_weight(T::MaxDisputes::get()) + } + + fn has_failed_max_weight() -> Weight { + T::WeightInfo::has_failed_weight(T::MaxDisputes::get()) + } + + fn on_global_dispute_max_weight() -> Weight { + T::WeightInfo::on_global_dispute_weight(T::MaxDisputes::get()) + } + + fn clear_max_weight() -> Weight { + T::WeightInfo::clear_weight(T::MaxDisputes::get()) + } + } + impl DisputeApi for Pallet where T: Config, @@ -295,22 +320,25 @@ mod pallet { type Moment = MomentOf; type Origin = T::Origin; - fn on_dispute(_: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn on_dispute( + _: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - Ok(()) - } - fn on_dispute_weight(_mdm_info: MDMWeightOf) -> Weight { - T::WeightInfo::on_dispute_weight() + let res = + ResultWithWeightInfo { result: (), weight: T::WeightInfo::on_dispute_weight() }; + + Ok(res) } fn on_resolution( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism @@ -322,20 +350,20 @@ mod pallet { let last_dispute = match disputes.last() { Some(l) => l, // if there are no disputes, then the market is resolved with the default report - None => return Ok(None), + None => { + return Ok(ResultWithWeightInfo { + result: None, + weight: T::WeightInfo::on_resolution_weight(disputes.len() as u32), + }); + } }; - Ok(Some(last_dispute.outcome.clone())) - } + let res = ResultWithWeightInfo { + result: Some(last_dispute.outcome.clone()), + weight: T::WeightInfo::on_resolution_weight(disputes.len() as u32), + }; - fn on_resolution_weight(mdm_info: MDMWeightOf) -> Weight { - match mdm_info { - MDMWeight::SimpleDisputes { market_id, market: _ } => { - let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; - T::WeightInfo::on_resolution_weight(disputes_len) - } - _ => T::WeightInfo::on_resolution_weight(T::MaxDisputes::get()), - } + Ok(res) } fn exchange( @@ -343,7 +371,7 @@ mod pallet { market: &MarketOf, resolved_outcome: &OutcomeReport, mut overall_imbalance: NegativeImbalanceOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism @@ -389,100 +417,94 @@ mod pallet { Disputes::::remove(market_id); - Ok(overall_imbalance) - } + let res = ResultWithWeightInfo { + result: overall_imbalance, + weight: T::WeightInfo::exchange_weight(disputes.len() as u32), + }; - fn exchange_weight(mdm_info: MDMWeightOf) -> Weight { - match mdm_info { - MDMWeight::SimpleDisputes { market_id, market: _ } => { - let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; - T::WeightInfo::exchange_weight(disputes_len) - } - _ => T::WeightInfo::exchange_weight(T::MaxDisputes::get()), - } + Ok(res) } fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result, DispatchError> { + ) -> Result>, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); let disputes = Disputes::::get(market_id); - Ok(Self::get_auto_resolve(disputes.as_slice(), market)) - } - fn get_auto_resolve_weight(mdm_info: MDMWeightOf) -> Weight { - match mdm_info { - MDMWeight::SimpleDisputes { market_id, market: _ } => { - let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; - T::WeightInfo::get_auto_resolve_weight(disputes_len) - } - _ => T::WeightInfo::get_auto_resolve_weight(T::MaxDisputes::get()), - } + let res = ResultWithWeightInfo { + result: Self::get_auto_resolve(disputes.as_slice(), market), + weight: T::WeightInfo::get_auto_resolve_weight(disputes.len() as u32), + }; + + Ok(res) } fn has_failed( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result { + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); let disputes = >::get(market_id); - Ok(disputes.len() == T::MaxDisputes::get() as usize) - } + let disputes_len = disputes.len() as u32; - fn has_failed_weight(mdm_info: MDMWeightOf) -> Weight { - match mdm_info { - MDMWeight::SimpleDisputes { market_id, market: _ } => { - let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; - T::WeightInfo::has_failed_weight(disputes_len) - } - _ => T::WeightInfo::has_failed_weight(T::MaxDisputes::get()), - } + let res = ResultWithWeightInfo { + result: disputes_len == T::MaxDisputes::get(), + weight: T::WeightInfo::has_failed_weight(disputes_len), + }; + + Ok(res) } fn on_global_dispute( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result>, DispatchError> { + ) -> Result< + ResultWithWeightInfo>>, + DispatchError, + > { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); - Ok(>::get(market_id) - .iter() - .map(|dispute| GlobalDisputeItem { - outcome: dispute.outcome.clone(), - owner: dispute.by.clone(), - initial_vote_amount: dispute.bond, - }) - .collect()) - } + let disputes_len = >::decode_len(market_id).unwrap_or(0) as u32; + + let res = ResultWithWeightInfo { + result: >::get(market_id) + .iter() + .map(|dispute| GlobalDisputeItem { + outcome: dispute.outcome.clone(), + owner: dispute.by.clone(), + initial_vote_amount: dispute.bond, + }) + .collect(), + weight: T::WeightInfo::on_global_dispute_weight(disputes_len), + }; - fn on_global_dispute_weight(mdm_info: MDMWeightOf) -> Weight { - match mdm_info { - MDMWeight::SimpleDisputes { market_id, market: _ } => { - let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; - T::WeightInfo::on_global_dispute_weight(disputes_len) - } - _ => T::WeightInfo::on_global_dispute_weight(T::MaxDisputes::get()), - } + Ok(res) } - fn clear(market_id: &Self::MarketId, market: &MarketOf) -> DispatchResult { + fn clear( + market_id: &Self::MarketId, + market: &MarketOf, + ) -> Result, DispatchError> { ensure!( market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, Error::::MarketDoesNotHaveSimpleDisputesMechanism ); + + let mut disputes_len = 0u32; // `Disputes` is emtpy unless the market is disputed, so this is just a defensive // check. if market.status == MarketStatus::Disputed { + disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; for dispute in Disputes::::take(market_id).iter() { T::AssetManager::unreserve_named( &Self::reserve_id(), @@ -492,17 +514,13 @@ mod pallet { ); } } - Ok(()) - } - fn clear_weight(mdm_info: MDMWeightOf) -> Weight { - match mdm_info { - MDMWeight::SimpleDisputes { market_id, market: _ } => { - let disputes_len = Disputes::::decode_len(market_id).unwrap_or(0) as u32; - T::WeightInfo::clear_weight(disputes_len) - } - _ => T::WeightInfo::clear_weight(T::MaxDisputes::get()), - } + let res = ResultWithWeightInfo { + result: (), + weight: T::WeightInfo::clear_weight(disputes_len), + }; + + Ok(res) } } diff --git a/zrml/simple-disputes/src/simple_disputes_pallet_api.rs b/zrml/simple-disputes/src/simple_disputes_pallet_api.rs index 3c723b624..221193d0f 100644 --- a/zrml/simple-disputes/src/simple_disputes_pallet_api.rs +++ b/zrml/simple-disputes/src/simple_disputes_pallet_api.rs @@ -15,6 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . -use zeitgeist_primitives::traits::DisputeApi; +use zeitgeist_primitives::traits::{DisputeApi, DisputeMaxWeightApi}; -pub trait SimpleDisputesPalletApi: DisputeApi {} +pub trait SimpleDisputesPalletApi: DisputeApi + DisputeMaxWeightApi {} diff --git a/zrml/simple-disputes/src/tests.rs b/zrml/simple-disputes/src/tests.rs index 19981e3b8..f5f35ba89 100644 --- a/zrml/simple-disputes/src/tests.rs +++ b/zrml/simple-disputes/src/tests.rs @@ -99,7 +99,7 @@ fn on_resolution_sets_the_last_dispute_of_disputed_markets_as_the_canonical_outc .unwrap(); Disputes::::insert(0, &disputes); assert_eq!( - &SimpleDisputes::on_resolution(&0, &market).unwrap().unwrap(), + &SimpleDisputes::on_resolution(&0, &market).unwrap().result.unwrap(), &disputes.last().unwrap().outcome ) }); From da9ef194f20f31b19c05760f0215297f1aa67110 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 21 Apr 2023 15:51:13 +0200 Subject: [PATCH 269/368] test delegations for reassign --- zrml/court/src/benchmarks.rs | 2 +- zrml/court/src/lib.rs | 45 +++++++---- zrml/court/src/tests.rs | 143 +++++++++++++++++++++++++++++++++++ zrml/court/src/weights.rs | 44 +++++------ 4 files changed, 194 insertions(+), 40 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 71d46e1aa..294d2994a 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -212,7 +212,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller), new_stake) delegate { - // jurors greater or equal to MaxDelegations, + // jurors greater or equal to MaxDelegations, // because we can not delegate to a non-existent juror let j in T::MaxDelegations::get()..(T::MaxJurors::get() - 1); let d in 1..T::MaxDelegations::get(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index ab302628c..5a40671cc 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -241,7 +241,7 @@ mod pallet { #[pallet::storage] pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; - // TODO make everything independent from the concept of market ids. + // TODO make everything independent from the concept of market ids. // TODO so that the integration of this pallet elsewhere is easier /// The randomly selected jurors with their vote. @@ -477,14 +477,14 @@ mod pallet { /// If the one of the delegated jurors is selected for a court case, /// the caller delegates the vote power to the drawn delegated juror. /// The delegator gets slashed or rewarded according to the delegated jurors decisions. - /// + /// /// # Arguments - /// + /// /// - `amount`: The total stake associated with the joining delegator. /// - `delegations`: The list of jurors to delegate the vote power to. - /// + /// /// # Weight - /// + /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. #[pallet::weight(T::WeightInfo::delegate(T::MaxJurors::get(), delegations.len() as u32))] #[transactional] @@ -971,27 +971,32 @@ mod pallet { Some(SelfInfo { slashable: draw.slashable, outcome }); } Vote::Delegated { delegated_stakes } => { + // TODO: test delegate and rewarding let delegator = draw.juror; for (j, delegated_stake) in delegated_stakes { // fill the delegations for each juror // [(juror_0, [(delegator_0, delegator_stake_0), ...]), // (juror_1, [(delegator_42, delegator_stake_42), ...]), ...] - let delegations = - &mut jurors_to_stakes.entry(j).or_default().delegations; + let jurors_to_stakes_entry = jurors_to_stakes.entry(j); + let juror_vote_with_stakes = jurors_to_stakes_entry.or_default(); + // future-proof binary search by key // because many delegators can back one juror // we might want to fastly find elements later on - match delegations.binary_search_by_key(&delegator, |(d, _)| d.clone()) { + match juror_vote_with_stakes + .delegations + .binary_search_by_key(&delegator, |(d, _)| d.clone()) + { Ok(i) => { - debug_assert!( - false, - "Delegator {:?} already in delegations.", - delegator - ); - delegations[i].1.saturating_add(delegated_stake); + juror_vote_with_stakes.delegations[i].1 = + juror_vote_with_stakes.delegations[i] + .1 + .saturating_add(delegated_stake); } Err(i) => { - delegations.insert(i, (delegator.clone(), delegated_stake)); + juror_vote_with_stakes + .delegations + .insert(i, (delegator.clone(), delegated_stake)); } } } @@ -1389,6 +1394,14 @@ mod pallet { weight.is_zero(), "Jurors who delegated shouldn't have voting weight." ); + debug_assert!( + delegated_stakes + .clone() + .into_iter() + .fold(Zero::zero(), |acc: BalanceOf, (_, stake)| acc + .saturating_add(stake)) + == slashable + ); Vote::Delegated { delegated_stakes } } else { Vote::Drawn @@ -1474,7 +1487,7 @@ mod pallet { // It is using necessary_jurors_weight (the number of atoms / draw weight) // for the last round times two because each delegator // could potentially add one juror account to the selections - + // new appeal round should have a fresh set of draws Ok(>::truncate_from(random_jurors)) } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 5936d718e..77fb94996 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1943,6 +1943,149 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { }); } +#[test] +fn reassign_juror_stakes_works_for_delegations() { + ExtBuilder::default().build().execute_with(|| { + fill_juror_pool(); + let market_id = initialize_court(); + + let amount = MinJurorStake::get() * 100; + assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); + assert_ok!(Court::join_court(Origin::signed(BOB), amount)); + assert_ok!(Court::join_court(Origin::signed(CHARLIE), amount)); + assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); + assert_ok!(Court::join_court(Origin::signed(EVE), amount)); + + let outcome = OutcomeReport::Scalar(42u128); + let salt = ::Hash::default(); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + + let wrong_outcome = OutcomeReport::Scalar(69u128); + + let alice_slashable = MinJurorStake::get(); + let bob_slashable = 2 * MinJurorStake::get(); + let charlie_slashable = 3 * MinJurorStake::get(); + let dave_slashable = 3 * MinJurorStake::get(); + let eve_slashable = 5 * MinJurorStake::get(); + + let delegated_stakes_charlie: crate::DelegatedStakesOf = + vec![(ALICE, 2 * MinJurorStake::get()), (BOB, MinJurorStake::get())] + .try_into() + .unwrap(); + + let delegated_stakes_dave: crate::DelegatedStakesOf = + vec![(ALICE, 2 * MinJurorStake::get()), (BOB, MinJurorStake::get())] + .try_into() + .unwrap(); + + let draws: crate::SelectedDrawsOf = vec![ + Draw { + juror: ALICE, + weight: 1, + vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + slashable: alice_slashable, + }, + Draw { + juror: EVE, + weight: 1, + vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + slashable: eve_slashable, + }, + Draw { + juror: BOB, + weight: 1, + vote: Vote::Revealed { commitment, outcome: wrong_outcome, salt }, + slashable: bob_slashable, + }, + Draw { + juror: CHARLIE, + weight: 1, + vote: Vote::Delegated { delegated_stakes: delegated_stakes_charlie.clone() }, + slashable: charlie_slashable, + }, + Draw { + juror: DAVE, + weight: 1, + vote: Vote::Delegated { delegated_stakes: delegated_stakes_dave.clone() }, + slashable: dave_slashable, + }, + ] + .try_into() + .unwrap(); + let last_draws = draws.clone(); + >::insert(market_id, draws); + + run_to_block(>::get() + 1); + + run_blocks( + CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, + ); + + let market = MarketCommons::market(&market_id).unwrap(); + let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().unwrap(); + assert_eq!(resolution_outcome, outcome); + + let free_alice_before = Balances::free_balance(ALICE); + let free_bob_before = Balances::free_balance(BOB); + let free_charlie_before = Balances::free_balance(CHARLIE); + let free_dave_before = Balances::free_balance(DAVE); + let free_eve_before = Balances::free_balance(EVE); + + let reward_pot = Court::reward_pot(&market_id); + let tardy_or_denounced_value = 5 * MinJurorStake::get(); + let _ = Balances::deposit(&reward_pot, tardy_or_denounced_value).unwrap(); + + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + + let bob_slashed = last_draws.iter().find(|draw| draw.juror == BOB).unwrap().slashable; + let charlie_delegated_bob_slashed = + delegated_stakes_charlie.iter().find(|(acc, _)| *acc == BOB).unwrap().1; + let dave_delegated_bob_slashed = + delegated_stakes_dave.iter().find(|(acc, _)| *acc == BOB).unwrap().1; + let slashed = bob_slashed + + charlie_delegated_bob_slashed + + dave_delegated_bob_slashed + + tardy_or_denounced_value; + + let charlie_delegated_alice_slashable = + delegated_stakes_charlie.iter().find(|(acc, _)| *acc == ALICE).unwrap().1; + let dave_delegated_alice_slashable = + delegated_stakes_dave.iter().find(|(acc, _)| *acc == ALICE).unwrap().1; + let winners_risked_amount = charlie_delegated_alice_slashable + + dave_delegated_alice_slashable + + alice_slashable + + eve_slashable; + + let alice_share = Perquintill::from_rational(alice_slashable, winners_risked_amount); + let free_alice_after = Balances::free_balance(ALICE); + assert_eq!(free_alice_after, free_alice_before + alice_share * slashed); + + let eve_share = Perquintill::from_rational(eve_slashable, winners_risked_amount); + let free_eve_after = Balances::free_balance(EVE); + assert_eq!(free_eve_after, free_eve_before + eve_share * slashed); + + let free_bob_after = Balances::free_balance(BOB); + assert_eq!(free_bob_after, free_bob_before - bob_slashed); + + let charlie_share = + Perquintill::from_rational(charlie_delegated_alice_slashable, winners_risked_amount); + let free_charlie_after = Balances::free_balance(CHARLIE); + let charlie_rewarded = charlie_share * slashed; + assert_eq!( + free_charlie_after, + free_charlie_before + charlie_rewarded - charlie_delegated_bob_slashed + ); + + let dave_share = + Perquintill::from_rational(dave_delegated_alice_slashable, winners_risked_amount); + let free_dave_after = Balances::free_balance(DAVE); + let dave_rewarded = dave_share * slashed; + assert_eq!(free_dave_after, free_dave_before + dave_rewarded - dave_delegated_bob_slashed); + + assert!(Balances::free_balance(&reward_pot).is_zero()); + }); +} + #[test] fn reassign_juror_stakes_rewards_treasury_if_no_winner() { ExtBuilder::default().build().execute_with(|| { diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 5592fdc34..23863ed67 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . - //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev @@ -46,19 +45,19 @@ use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within /// zrml_court (automatically generated) pub trait WeightInfoZeitgeist { - fn join_court(j: u32, ) -> Weight; - fn delegate(j: u32, d: u32, ) -> Weight; - fn prepare_exit_court(j: u32, ) -> Weight; + fn join_court(j: u32) -> Weight; + fn delegate(j: u32, d: u32) -> Weight; + fn prepare_exit_court(j: u32) -> Weight; fn exit_court_remove() -> Weight; fn exit_court_set() -> Weight; - fn vote(d: u32, ) -> Weight; - fn denounce_vote(d: u32, ) -> Weight; - fn reveal_vote(d: u32, ) -> Weight; - fn appeal(j: u32, a: u32, r: u32, f: u32, ) -> Weight; - fn reassign_juror_stakes(d: u32, ) -> Weight; + fn vote(d: u32) -> Weight; + fn denounce_vote(d: u32) -> Weight; + fn reveal_vote(d: u32) -> Weight; + fn appeal(j: u32, a: u32, r: u32, f: u32) -> Weight; + fn reassign_juror_stakes(d: u32) -> Weight; fn set_inflation() -> Weight; - fn handle_inflation(j: u32, ) -> Weight; - fn select_jurors(a: u32, ) -> Weight; + fn handle_inflation(j: u32) -> Weight; + fn select_jurors(a: u32) -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -67,7 +66,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn join_court(j: u32, ) -> Weight { + fn join_court(j: u32) -> Weight { Weight::from_ref_time(37_055_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(91_000).saturating_mul(j.into())) @@ -77,7 +76,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:6 w:1) // Storage: Balances Locks (r:1 w:1) - fn delegate(j: u32, d: u32, ) -> Weight { + fn delegate(j: u32, d: u32) -> Weight { Weight::from_ref_time(47_313_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(122_000).saturating_mul(j.into())) @@ -89,7 +88,7 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court Jurors (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) - fn prepare_exit_court(j: u32, ) -> Weight { + fn prepare_exit_court(j: u32) -> Weight { Weight::from_ref_time(22_189_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(85_000).saturating_mul(j.into())) @@ -112,7 +111,7 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn vote(d: u32, ) -> Weight { + fn vote(d: u32) -> Weight { Weight::from_ref_time(49_834_000) // Standard Error: 3_000 .saturating_add(Weight::from_ref_time(231_000).saturating_mul(d.into())) @@ -123,7 +122,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn denounce_vote(d: u32, ) -> Weight { + fn denounce_vote(d: u32) -> Weight { Weight::from_ref_time(38_119_000) // Standard Error: 1_000 .saturating_add(Weight::from_ref_time(220_000).saturating_mul(d.into())) @@ -134,7 +133,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn reveal_vote(d: u32, ) -> Weight { + fn reveal_vote(d: u32) -> Weight { Weight::from_ref_time(73_151_000) // Standard Error: 9_000 .saturating_add(Weight::from_ref_time(436_000).saturating_mul(d.into())) @@ -151,7 +150,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Balances Reserves (r:1 w:1) - fn appeal(j: u32, a: u32, r: u32, _f: u32, ) -> Weight { + fn appeal(j: u32, a: u32, r: u32, _f: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 11_000 .saturating_add(Weight::from_ref_time(1_750_000).saturating_mul(j.into())) @@ -168,7 +167,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:5 w:5) // Storage: System Account (r:6 w:5) - fn reassign_juror_stakes(d: u32, ) -> Weight { + fn reassign_juror_stakes(d: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 18_000 .saturating_add(Weight::from_ref_time(66_657_000).saturating_mul(d.into())) @@ -179,13 +178,12 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court YearlyInflation (r:0 w:1) fn set_inflation() -> Weight { - Weight::from_ref_time(10_000_000) - .saturating_add(T::DbWeight::get().writes(1)) + Weight::from_ref_time(10_000_000).saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court YearlyInflation (r:1 w:0) // Storage: Court JurorPool (r:1 w:0) // Storage: System Account (r:1 w:1) - fn handle_inflation(j: u32, ) -> Weight { + fn handle_inflation(j: u32) -> Weight { Weight::from_ref_time(1_723_972_000) // Standard Error: 98_000 .saturating_add(Weight::from_ref_time(14_481_000).saturating_mul(j.into())) @@ -197,7 +195,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) // Storage: Court Jurors (r:9 w:5) - fn select_jurors(a: u32, ) -> Weight { + fn select_jurors(a: u32) -> Weight { Weight::from_ref_time(208_843_000) // Standard Error: 1_953_000 .saturating_add(Weight::from_ref_time(387_998_000).saturating_mul(a.into())) From 4c0a2f7923b34541add3f1b54b83fa59965bd238 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 21 Apr 2023 16:05:46 +0200 Subject: [PATCH 270/368] correct benchmarks and test --- zrml/court/src/benchmarks.rs | 4 ++-- zrml/prediction-markets/src/lib.rs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 294d2994a..9e170343e 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -214,7 +214,7 @@ benchmarks! { delegate { // jurors greater or equal to MaxDelegations, // because we can not delegate to a non-existent juror - let j in T::MaxDelegations::get()..(T::MaxJurors::get() - 1); + let j in 5..(T::MaxJurors::get() - 1); let d in 1..T::MaxDelegations::get(); fill_pool::(j)?; @@ -464,7 +464,7 @@ benchmarks! { } reassign_juror_stakes { - let d in T::MaxDelegations::get()..T::MaxSelectedDraws::get(); + let d in 5..T::MaxSelectedDraws::get(); debug_assert!(T::MaxDelegations::get() < T::MaxSelectedDraws::get()); // just to initialize the court diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index ba7a9e69e..31ac4e65b 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1423,7 +1423,10 @@ mod pallet { ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); ensure!( - matches!(market.dispute_mechanism, MarketDisputeMechanism::Court), + matches!( + market.dispute_mechanism, + MarketDisputeMechanism::Court | MarketDisputeMechanism::SimpleDisputes + ), Error::::InvalidDisputeMechanism ); From e2eba05635ac1ddcd4f9cd38c8e297cee8e02ee6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 21 Apr 2023 16:17:17 +0200 Subject: [PATCH 271/368] remove comment --- zrml/court/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 5a40671cc..060d4a491 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -971,7 +971,6 @@ mod pallet { Some(SelfInfo { slashable: draw.slashable, outcome }); } Vote::Delegated { delegated_stakes } => { - // TODO: test delegate and rewarding let delegator = draw.juror; for (j, delegated_stake) in delegated_stakes { // fill the delegations for each juror From 1d25aeb1f54c17d09fc1db1f6fa6bfbb21a18472 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 24 Apr 2023 15:16:46 +0200 Subject: [PATCH 272/368] add API benchmarks --- primitives/src/types.rs | 2 + runtime/common/src/lib.rs | 1 + zrml/court/src/benchmarks.rs | 157 ++++++++++++++++++++++++++++++++++- zrml/court/src/lib.rs | 36 ++------ 4 files changed, 161 insertions(+), 35 deletions(-) diff --git a/primitives/src/types.rs b/primitives/src/types.rs index a380cf656..be48aeb3e 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -168,3 +168,5 @@ pub struct XcmMetadata { /// Should be updated regularly. pub fee_factor: Option, } + +pub type VoteItem = OutcomeReport; \ No newline at end of file diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index cf4b53ac1..11c31b871 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -954,6 +954,7 @@ macro_rules! impl_config_traits { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; + type VoteItem = zeitgeist_primitives::types::VoteItem; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index a795cfbba..3cbf51374 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -24,14 +24,13 @@ extern crate alloc; use crate::{ - market_mock, types::{CourtStatus, Draw, JurorInfo, JurorPoolItem, Vote}, AppealInfo, BalanceOf, Call, Config, Courts, DelegatedStakesOf, JurorPool, Jurors, MarketOf, Pallet as Court, Pallet, RequestBlock, SelectedDraws, }; use alloc::{vec, vec::Vec}; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; -use frame_support::traits::{Currency, Get}; +use frame_support::traits::{Currency, Get, NamedReservableCurrency}; use frame_system::RawOrigin; use sp_arithmetic::Perbill; use sp_runtime::{ @@ -199,6 +198,21 @@ where Ok(()) } +fn apply_revealed_draws(market_id: crate::MarketIdOf) +where + T: Config, +{ + let winner_outcome = OutcomeReport::Scalar(0u128); + let mut draws = >::get(market_id); + // change draws to have revealed votes + for draw in draws.iter_mut() { + let salt = Default::default(); + let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); + draw.vote = Vote::Revealed { commitment, outcome: winner_outcome.clone(), salt }; + } + >::insert(market_id, draws); +} + benchmarks! { join_court { let j in 0..(T::MaxJurors::get() - 1); @@ -546,14 +560,149 @@ benchmarks! { let _ = Court::::select_jurors(a as usize).unwrap(); } - on_dispute_weight { + on_dispute { + let j in 5..T::MaxJurors::get(); + let r in 0..62; + + let now = >::block_number(); + let pre_vote_end = now + 1u64.saturated_into::(); + >::put(pre_vote_end); + + let appeal_end = pre_vote_end + + T::CourtVotePeriod::get() + + T::CourtAggregationPeriod::get() + + T::CourtAppealPeriod::get(); + + for i in 0..r { + let market_id_i = (i + 100).saturated_into::>(); + T::DisputeResolution::add_auto_resolve(&market_id_i, appeal_end).unwrap(); + } + + fill_pool::(j)?; + let market_id = 0u32.into(); - let market = market_mock::(); + let market = get_market::(); T::MarketCommons::push_market(market.clone()).unwrap(); }: { Court::::on_dispute(&market_id, &market).unwrap(); } + on_resolution { + let j in 5..T::MaxJurors::get(); + let d in 1..T::MaxSelectedDraws::get(); + + fill_pool::(j)?; + + let market_id = setup_court::()?; + let market = get_market::(); + + fill_draws::(market_id, d)?; + + let winner_outcome = OutcomeReport::Scalar(0u128); + let mut draws = >::get(market_id); + // change draws to have revealed votes + for draw in draws.iter_mut() { + let salt = Default::default(); + let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); + draw.vote = Vote::Revealed { + commitment, + outcome: winner_outcome.clone(), + salt, + }; + } + >::insert(market_id, draws); + }: { + Court::::on_resolution(&market_id, &market).unwrap(); + } + + exchange { + let a in 0..T::MaxAppeals::get(); + + fill_pool::(5)?; + let market_id = setup_court::()?; + let market = get_market::(); + + let mut court = >::get(market_id).unwrap(); + + let resolved_outcome = OutcomeReport::Scalar(0u128); + for i in 0..a { + let backer = account("backer", i, 0); + let bond = T::MinJurorStake::get(); + let _ = T::Currency::deposit_creating(&backer, bond); + T::Currency::reserve_named(&Court::::reserve_id(), &backer, bond).unwrap(); + let appeal_info = AppealInfo { + backer, + bond, + appealed_outcome: resolved_outcome.clone(), + }; + court.appeals.try_push(appeal_info).unwrap(); + } + >::insert(market_id, court); + }: { + Court::::exchange(&market_id, &market, &resolved_outcome, Default::default()).unwrap(); + } + + get_auto_resolve { + fill_pool::(5)?; + let market_id = setup_court::()?; + let market = get_market::(); + }: { + Court::::get_auto_resolve(&market_id, &market).unwrap(); + } + + has_failed { + fill_pool::(5)?; + let market_id = setup_court::()?; + let market = get_market::(); + }: { + Court::::has_failed(&market_id, &market).unwrap(); + } + + on_global_dispute { + let a in 0..T::MaxAppeals::get(); + let d in 1..T::MaxSelectedDraws::get(); + + fill_pool::(5)?; + let market_id = setup_court::()?; + let market = get_market::(); + + fill_draws::(market_id, d)?; + apply_revealed_draws::(market_id); + + let resolved_outcome = OutcomeReport::Scalar(0u128); + + let mut court = >::get(market_id).unwrap(); + for i in 0..a { + let backer = account("backer", i, 0); + let bond = T::MinJurorStake::get(); + let _ = T::Currency::deposit_creating(&backer, bond); + T::Currency::reserve_named(&Court::::reserve_id(), &backer, bond).unwrap(); + let appeal_info = AppealInfo { + backer, + bond, + appealed_outcome: resolved_outcome.clone(), + }; + court.appeals.try_push(appeal_info).unwrap(); + } + >::insert(market_id, court); + }: { + Court::::on_global_dispute(&market_id, &market).unwrap(); + } + + clear { + let d in 1..T::MaxSelectedDraws::get(); + + fill_pool::(5)?; + + let market_id = setup_court::()?; + let market = get_market::(); + + fill_draws::(market_id, d)?; + apply_revealed_draws::(market_id); + }: { + Court::::clear(&market_id, &market).unwrap(); + } + impl_benchmark_test_suite!( Court, crate::mock::ExtBuilder::default().build(), diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bafcf8be2..6fdf3f664 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -57,6 +57,7 @@ use sp_runtime::{ traits::{AccountIdConversion, Hash, Saturating, StaticLookup, Zero}, DispatchError, Perbill, SaturatedConversion, }; +use frame_support::Parameter; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}, types::{ @@ -64,6 +65,7 @@ use zeitgeist_primitives::{ ResultWithWeightInfo, }, }; +use frame_support::pallet_prelude::Member; use zrml_market_commons::MarketCommonsPalletApi; mod benchmarks; @@ -175,6 +177,9 @@ mod pallet { #[pallet::constant] type TreasuryPalletId: Get; + /// The vote item to be used for the court. + type VoteItem: Parameter + Member + Clone + Default + Ord; + /// Weights generated by benchmarks type WeightInfo: WeightInfoZeitgeist; } @@ -1919,7 +1924,6 @@ mod pallet { } // TODO all funds to treasury? - let res = ResultWithWeightInfo { result: overall_imbalance, weight: T::WeightInfo::exchange_weight(), @@ -2151,33 +2155,3 @@ impl Pallet { Ok(jurors_len) } } - -#[cfg(any(feature = "runtime-benchmarks", test))] -pub(crate) fn market_mock() -> MarketOf -where - T: crate::Config, -{ - use zeitgeist_primitives::types::{MarketBonds, ScoringRule}; - - zeitgeist_primitives::types::Market { - base_asset: Asset::Ztg, - creation: zeitgeist_primitives::types::MarketCreation::Permissionless, - creator_fee: 0, - creator: T::CourtPalletId::get().into_account_truncating(), - market_type: zeitgeist_primitives::types::MarketType::Scalar(0..=100), - dispute_mechanism: zeitgeist_primitives::types::MarketDisputeMechanism::Court, - metadata: Default::default(), - oracle: T::CourtPalletId::get().into_account_truncating(), - period: zeitgeist_primitives::types::MarketPeriod::Block(Default::default()), - deadlines: zeitgeist_primitives::types::Deadlines { - grace_period: 1_u32.into(), - oracle_duration: 1_u32.into(), - dispute_duration: 1_u32.into(), - }, - report: None, - resolved_outcome: None, - scoring_rule: ScoringRule::CPMM, - status: zeitgeist_primitives::types::MarketStatus::Disputed, - bonds: MarketBonds::default(), - } -} From 7dd291f9bb3a4b85903329676d8839c7b3d994b2 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 24 Apr 2023 16:31:09 +0200 Subject: [PATCH 273/368] update weights --- zrml/court/src/weights.rs | 204 ++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 73 deletions(-) diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index e47206835..c7a1cc445 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -15,10 +15,11 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . + //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-20, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-24, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -45,26 +46,26 @@ use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within /// zrml_court (automatically generated) pub trait WeightInfoZeitgeist { - fn join_court(j: u32) -> Weight; - fn delegate(j: u32, d: u32) -> Weight; - fn prepare_exit_court(j: u32) -> Weight; + fn join_court(j: u32, ) -> Weight; + fn delegate(j: u32, d: u32, ) -> Weight; + fn prepare_exit_court(j: u32, ) -> Weight; fn exit_court_remove() -> Weight; fn exit_court_set() -> Weight; - fn vote(d: u32) -> Weight; - fn denounce_vote(d: u32) -> Weight; - fn reveal_vote(d: u32) -> Weight; - fn appeal(j: u32, a: u32, r: u32, f: u32) -> Weight; - fn reassign_juror_stakes(d: u32) -> Weight; + fn vote(d: u32, ) -> Weight; + fn denounce_vote(d: u32, ) -> Weight; + fn reveal_vote(d: u32, ) -> Weight; + fn appeal(j: u32, a: u32, r: u32, f: u32, ) -> Weight; + fn reassign_juror_stakes(d: u32, ) -> Weight; fn set_inflation() -> Weight; - fn handle_inflation(j: u32) -> Weight; - fn select_jurors(a: u32) -> Weight; - fn on_dispute_weight() -> Weight; - fn on_resolution_weight() -> Weight; - fn exchange_weight() -> Weight; - fn get_auto_resolve_weight() -> Weight; - fn has_failed_weight() -> Weight; - fn on_global_dispute_weight() -> Weight; - fn clear_weight() -> Weight; + fn handle_inflation(j: u32, ) -> Weight; + fn select_jurors(a: u32, ) -> Weight; + fn on_dispute(j: u32, r: u32, ) -> Weight; + fn on_resolution(j: u32, d: u32, ) -> Weight; + fn exchange(a: u32, ) -> Weight; + fn get_auto_resolve() -> Weight; + fn has_failed() -> Weight; + fn on_global_dispute(a: u32, d: u32, ) -> Weight; + fn clear(d: u32, ) -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -73,39 +74,39 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn join_court(j: u32) -> Weight { - Weight::from_ref_time(37_055_000) + fn join_court(j: u32, ) -> Weight { + Weight::from_ref_time(33_635_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(91_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(98_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:6 w:1) // Storage: Balances Locks (r:1 w:1) - fn delegate(j: u32, d: u32) -> Weight { - Weight::from_ref_time(47_313_000) + fn delegate(j: u32, d: u32, ) -> Weight { + Weight::from_ref_time(48_104_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(122_000).saturating_mul(j.into())) - // Standard Error: 61_000 - .saturating_add(Weight::from_ref_time(1_227_000).saturating_mul(d.into())) + // Standard Error: 45_000 + .saturating_add(Weight::from_ref_time(447_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Court Jurors (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) - fn prepare_exit_court(j: u32) -> Weight { - Weight::from_ref_time(22_189_000) + fn prepare_exit_court(j: u32, ) -> Weight { + Weight::from_ref_time(19_274_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(85_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(84_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn exit_court_remove() -> Weight { - Weight::from_ref_time(38_000_000) + Weight::from_ref_time(39_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -118,10 +119,10 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn vote(d: u32) -> Weight { - Weight::from_ref_time(49_834_000) - // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(231_000).saturating_mul(d.into())) + fn vote(d: u32, ) -> Weight { + Weight::from_ref_time(49_081_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(106_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -129,10 +130,10 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn denounce_vote(d: u32) -> Weight { - Weight::from_ref_time(38_119_000) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(220_000).saturating_mul(d.into())) + fn denounce_vote(d: u32, ) -> Weight { + Weight::from_ref_time(39_078_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(124_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -140,10 +141,10 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn reveal_vote(d: u32) -> Weight { - Weight::from_ref_time(73_151_000) - // Standard Error: 9_000 - .saturating_add(Weight::from_ref_time(436_000).saturating_mul(d.into())) + fn reveal_vote(d: u32, ) -> Weight { + Weight::from_ref_time(66_728_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(91_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -157,14 +158,12 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Balances Reserves (r:1 w:1) - fn appeal(j: u32, a: u32, r: u32, _f: u32) -> Weight { + fn appeal(j: u32, a: u32, _r: u32, _f: u32, ) -> Weight { Weight::from_ref_time(0) - // Standard Error: 11_000 - .saturating_add(Weight::from_ref_time(1_750_000).saturating_mul(j.into())) - // Standard Error: 4_469_000 - .saturating_add(Weight::from_ref_time(835_228_000).saturating_mul(a.into())) - // Standard Error: 180_000 - .saturating_add(Weight::from_ref_time(9_074_000).saturating_mul(r.into())) + // Standard Error: 3_000 + .saturating_add(Weight::from_ref_time(1_390_000).saturating_mul(j.into())) + // Standard Error: 1_348_000 + .saturating_add(Weight::from_ref_time(630_362_000).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(13)) .saturating_add(T::DbWeight::get().reads((28_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(8)) @@ -174,10 +173,10 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:5 w:5) // Storage: System Account (r:6 w:5) - fn reassign_juror_stakes(d: u32) -> Weight { + fn reassign_juror_stakes(d: u32, ) -> Weight { Weight::from_ref_time(0) - // Standard Error: 18_000 - .saturating_add(Weight::from_ref_time(66_657_000).saturating_mul(d.into())) + // Standard Error: 8_000 + .saturating_add(Weight::from_ref_time(41_797_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -185,15 +184,16 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court YearlyInflation (r:0 w:1) fn set_inflation() -> Weight { - Weight::from_ref_time(10_000_000).saturating_add(T::DbWeight::get().writes(1)) + Weight::from_ref_time(6_000_000) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court YearlyInflation (r:1 w:0) // Storage: Court JurorPool (r:1 w:0) // Storage: System Account (r:1 w:1) - fn handle_inflation(j: u32) -> Weight { - Weight::from_ref_time(1_723_972_000) - // Standard Error: 98_000 - .saturating_add(Weight::from_ref_time(14_481_000).saturating_mul(j.into())) + fn handle_inflation(j: u32, ) -> Weight { + Weight::from_ref_time(0) + // Standard Error: 25_000 + .saturating_add(Weight::from_ref_time(14_531_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) @@ -202,34 +202,92 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) // Storage: Court Jurors (r:9 w:5) - fn select_jurors(a: u32) -> Weight { - Weight::from_ref_time(208_843_000) - // Standard Error: 1_953_000 - .saturating_add(Weight::from_ref_time(387_998_000).saturating_mul(a.into())) + fn select_jurors(a: u32, ) -> Weight { + Weight::from_ref_time(226_140_000) + // Standard Error: 2_247_000 + .saturating_add(Weight::from_ref_time(507_706_000).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().reads((12_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes((12_u64).saturating_mul(a.into()))) } - fn on_dispute_weight() -> Weight { - Weight::from_ref_time(0) + // Storage: Court Courts (r:1 w:1) + // Storage: Court JurorPool (r:1 w:1) + // Storage: Court JurorsSelectionNonce (r:1 w:1) + // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) + // Storage: Court Jurors (r:4 w:4) + // Storage: Court RequestBlock (r:1 w:0) + // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) + // Storage: Court SelectedDraws (r:0 w:1) + fn on_dispute(j: u32, r: u32, ) -> Weight { + Weight::from_ref_time(65_336_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(164_000).saturating_mul(j.into())) + // Standard Error: 10_000 + .saturating_add(Weight::from_ref_time(185_000).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11)) + .saturating_add(T::DbWeight::get().writes(10)) } - fn on_resolution_weight() -> Weight { + // Storage: Court Courts (r:1 w:1) + // Storage: Court SelectedDraws (r:1 w:0) + // Storage: MarketCommons Markets (r:1 w:0) + // Storage: Court Jurors (r:94 w:94) + fn on_resolution(j: u32, d: u32, ) -> Weight { Weight::from_ref_time(0) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(99_000).saturating_mul(j.into())) + // Standard Error: 3_000 + .saturating_add(Weight::from_ref_time(4_737_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) } - fn exchange_weight() -> Weight { - Weight::from_ref_time(0) + // Storage: Court Courts (r:1 w:0) + // Storage: Balances Reserves (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn exchange(a: u32, ) -> Weight { + Weight::from_ref_time(13_802_000) + // Standard Error: 30_000 + .saturating_add(Weight::from_ref_time(21_667_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(a.into()))) } - fn get_auto_resolve_weight() -> Weight { - Weight::from_ref_time(0) + // Storage: Court Courts (r:1 w:0) + fn get_auto_resolve() -> Weight { + Weight::from_ref_time(7_000_000) + .saturating_add(T::DbWeight::get().reads(1)) } - fn has_failed_weight() -> Weight { - Weight::from_ref_time(0) + // Storage: Court JurorPool (r:1 w:0) + // Storage: Court Courts (r:1 w:0) + // Storage: MarketCommons Markets (r:1 w:0) + fn has_failed() -> Weight { + Weight::from_ref_time(17_000_000) + .saturating_add(T::DbWeight::get().reads(3)) } - fn on_global_dispute_weight() -> Weight { - Weight::from_ref_time(0) + // Storage: Court Courts (r:1 w:1) + // Storage: Court SelectedDraws (r:1 w:1) + // Storage: Court Jurors (r:94 w:94) + fn on_global_dispute(_a: u32, d: u32, ) -> Weight { + Weight::from_ref_time(12_713_000) + // Standard Error: 1_000 + .saturating_add(Weight::from_ref_time(4_069_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) } - fn clear_weight() -> Weight { - Weight::from_ref_time(0) + // Storage: Court SelectedDraws (r:1 w:1) + // Storage: Court Jurors (r:1 w:1) + // Storage: Court Courts (r:0 w:1) + fn clear(d: u32, ) -> Weight { + Weight::from_ref_time(9_761_000) + // Standard Error: 1_000 + .saturating_add(Weight::from_ref_time(4_033_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) } } From 1720ccff20155b1b8c0d88df6f33d8ca0a77fb32 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 25 Apr 2023 10:09:46 +0200 Subject: [PATCH 274/368] use generic vote item instead of outcome report --- primitives/src/types.rs | 2 +- zrml/court/src/benchmarks.rs | 35 +++-- zrml/court/src/lib.rs | 217 ++++++++++++++++------------ zrml/court/src/mock.rs | 3 +- zrml/court/src/tests.rs | 100 +++++++------ zrml/court/src/traits.rs | 5 + zrml/court/src/types.rs | 52 +++---- zrml/court/src/weights.rs | 70 +++++---- zrml/prediction-markets/src/mock.rs | 2 + 9 files changed, 268 insertions(+), 218 deletions(-) create mode 100644 zrml/court/src/traits.rs diff --git a/primitives/src/types.rs b/primitives/src/types.rs index be48aeb3e..52467c3d7 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -169,4 +169,4 @@ pub struct XcmMetadata { pub fee_factor: Option, } -pub type VoteItem = OutcomeReport; \ No newline at end of file +pub type VoteItem = OutcomeReport; diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 3cbf51374..c0bbe5207 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -208,7 +208,7 @@ where for draw in draws.iter_mut() { let salt = Default::default(); let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); - draw.vote = Vote::Revealed { commitment, outcome: winner_outcome.clone(), salt }; + draw.vote = Vote::Revealed { commitment, vote_item: winner_outcome.clone().into(), salt }; } >::insert(market_id, draws); } @@ -358,7 +358,7 @@ benchmarks! { >::insert(market_id, draws); >::set_block_number(pre_vote + 1u64.saturated_into::()); - }: _(RawOrigin::Signed(caller), market_id, denounced_juror_unlookup, outcome, salt) + }: _(RawOrigin::Signed(caller), market_id, denounced_juror_unlookup, outcome.into(), salt) reveal_vote { let d in 1..T::MaxSelectedDraws::get(); @@ -397,7 +397,7 @@ benchmarks! { >::insert(market_id, draws); >::set_block_number(vote_end + 1u64.saturated_into::()); - }: _(RawOrigin::Signed(caller), market_id, outcome, salt) + }: _(RawOrigin::Signed(caller), market_id, outcome.into(), salt) appeal { // from 47 because in the last appeal round we need at least 47 jurors @@ -428,7 +428,7 @@ benchmarks! { let appeal_info = AppealInfo { backer: account("backer", i, 0), bond: crate::get_appeal_bond::(i as usize), - appealed_outcome: OutcomeReport::Scalar(0u128), + appealed_vote_item: OutcomeReport::Scalar(0u128).into(), }; court.appeals.try_push(appeal_info).unwrap(); } @@ -447,12 +447,12 @@ benchmarks! { prepare_exit_at: None, delegations: Default::default(), }); - let outcome = OutcomeReport::Scalar(i as u128); - let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let vote_item: T::VoteItem = OutcomeReport::Scalar(i as u128).into(); + let commitment = T::Hashing::hash_of(&(juror.clone(), vote_item.clone(), salt)); let draw = Draw { juror, - vote: Vote::Revealed { commitment, outcome, salt }, + vote: Vote::Revealed { commitment, vote_item, salt }, weight: 1u32, slashable: >::zero() }; @@ -492,7 +492,7 @@ benchmarks! { let mut court = >::get(market_id).unwrap(); let winner_outcome = OutcomeReport::Scalar(0u128); let wrong_outcome = OutcomeReport::Scalar(1u128); - court.status = CourtStatus::Closed { winner: winner_outcome.clone() }; + court.status = CourtStatus::Closed { winner: winner_outcome.clone().into() }; >::insert(market_id, court); let salt = Default::default(); @@ -512,15 +512,15 @@ benchmarks! { let draw = if i < T::MaxDelegations::get() { delegated_stakes.try_push((juror.clone(), T::MinJurorStake::get())).unwrap(); - let outcome = if i % 2 == 0 { - wrong_outcome.clone() + let vote_item: T::VoteItem = if i % 2 == 0 { + wrong_outcome.clone().into() } else { - winner_outcome.clone() + winner_outcome.clone().into() }; - let commitment = T::Hashing::hash_of(&(juror.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(juror.clone(), vote_item.clone(), salt)); Draw { juror, - vote: Vote::Revealed { commitment, outcome, salt }, + vote: Vote::Revealed { commitment, vote_item, salt }, weight: 1u32, slashable: T::MinJurorStake::get(), } @@ -588,10 +588,9 @@ benchmarks! { } on_resolution { - let j in 5..T::MaxJurors::get(); let d in 1..T::MaxSelectedDraws::get(); - fill_pool::(j)?; + fill_pool::(5)?; let market_id = setup_court::()?; let market = get_market::(); @@ -606,7 +605,7 @@ benchmarks! { let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); draw.vote = Vote::Revealed { commitment, - outcome: winner_outcome.clone(), + vote_item: winner_outcome.clone().into(), salt, }; } @@ -633,7 +632,7 @@ benchmarks! { let appeal_info = AppealInfo { backer, bond, - appealed_outcome: resolved_outcome.clone(), + appealed_vote_item: resolved_outcome.clone().into(), }; court.appeals.try_push(appeal_info).unwrap(); } @@ -680,7 +679,7 @@ benchmarks! { let appeal_info = AppealInfo { backer, bond, - appealed_outcome: resolved_outcome.clone(), + appealed_vote_item: resolved_outcome.clone().into(), }; court.appeals.try_push(appeal_info).unwrap(); } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 6fdf3f664..59cefdfb7 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -36,15 +36,15 @@ use frame_support::{ dispatch::DispatchResult, ensure, log, pallet_prelude::{ - ConstU32, DispatchResultWithPostInfo, EnsureOrigin, Hooks, OptionQuery, StorageMap, - StorageValue, ValueQuery, Weight, + ConstU32, DispatchResultWithPostInfo, EnsureOrigin, Hooks, MaxEncodedLen, Member, + OptionQuery, StorageMap, StorageValue, ValueQuery, Weight, }, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, NamedReservableCurrency, OnUnbalanced, Randomness, ReservableCurrency, StorageVersion, WithdrawReasons, }, - transactional, Blake2_128Concat, BoundedVec, PalletId, + transactional, Blake2_128Concat, BoundedVec, PalletId, Parameter, }; use frame_system::{ ensure_signed, @@ -57,7 +57,6 @@ use sp_runtime::{ traits::{AccountIdConversion, Hash, Saturating, StaticLookup, Zero}, DispatchError, Perbill, SaturatedConversion, }; -use frame_support::Parameter; use zeitgeist_primitives::{ traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}, types::{ @@ -65,7 +64,6 @@ use zeitgeist_primitives::{ ResultWithWeightInfo, }, }; -use frame_support::pallet_prelude::Member; use zrml_market_commons::MarketCommonsPalletApi; mod benchmarks; @@ -178,7 +176,15 @@ mod pallet { type TreasuryPalletId: Get; /// The vote item to be used for the court. - type VoteItem: Parameter + Member + Clone + Default + Ord; + type VoteItem: Parameter + + Member + + Clone + + Ord + + PartialEq + + Eq + + MaxEncodedLen + + Into + + From; /// Weights generated by benchmarks type WeightInfo: WeightInfoZeitgeist; @@ -211,22 +217,32 @@ mod pallet { pub(crate) type HashOf = ::Hash; pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; + pub(crate) type CourtOf = + CourtInfo<::BlockNumber, AppealsOf, ::VoteItem>; pub(crate) type DelegatedStakesOf = BoundedVec<(AccountIdOf, BalanceOf), ::MaxDelegations>; pub(crate) type SelectionValueOf = SelectionValue, DelegatedStakesOf>; pub(crate) type DelegationsOf = BoundedVec, ::MaxDelegations>; - pub(crate) type VoteOf = Vote, DelegatedStakesOf>; - pub(crate) type JurorVoteWithStakesOf = JurorVoteWithStakes, BalanceOf>; + pub(crate) type VoteOf = Vote, DelegatedStakesOf, ::VoteItem>; + pub(crate) type JurorVoteWithStakesOf = + JurorVoteWithStakes, BalanceOf, ::VoteItem>; pub(crate) type JurorInfoOf = JurorInfo, BlockNumberFor, DelegationsOf>; pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; - pub(crate) type DrawOf = Draw, BalanceOf, HashOf, DelegatedStakesOf>; + pub(crate) type DrawOf = Draw< + AccountIdOf, + BalanceOf, + HashOf, + DelegatedStakesOf, + ::VoteItem, + >; pub(crate) type SelectedDrawsOf = BoundedVec, ::MaxSelectedDraws>; - pub(crate) type AppealOf = AppealInfo, BalanceOf>; + pub(crate) type AppealOf = AppealInfo, BalanceOf, ::VoteItem>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; - pub(crate) type CommitmentMatcherOf = CommitmentMatcher, HashOf>; - pub(crate) type RawCommitmentOf = RawCommitment, HashOf>; + pub(crate) type CommitmentMatcherOf = + CommitmentMatcher, HashOf, ::VoteItem>; + pub(crate) type RawCommitmentOf = + RawCommitment, HashOf, ::VoteItem>; pub type CacheSize = ConstU32<64>; #[pallet::pallet] @@ -306,7 +322,7 @@ mod pallet { JurorRevealedVote { juror: T::AccountId, market_id: MarketIdOf, - outcome: OutcomeReport, + vote_item: T::VoteItem, salt: T::Hash, }, /// A juror vote has been denounced. @@ -314,7 +330,7 @@ mod pallet { denouncer: T::AccountId, juror: T::AccountId, market_id: MarketIdOf, - outcome: OutcomeReport, + vote_item: T::VoteItem, salt: T::Hash, }, /// A delegator has delegated their stake to jurors. @@ -353,7 +369,7 @@ mod pallet { OnlyJurorsCanReveal, /// The vote is not commitment. VoteAlreadyRevealed, - /// The outcome and salt reveal do not match the commitment vote. + /// The vote item and salt reveal do not match the commitment vote. InvalidReveal, /// No court for this market id was found. CourtNotFound, @@ -637,7 +653,7 @@ mod pallet { /// # Arguments /// /// - `market_id`: The identifier of the court. - /// - `commitment_vote`: A hash which consists of `juror ++ outcome ++ salt`. + /// - `commitment_vote`: A hash which consists of `juror ++ vote_item ++ salt`. /// /// # Weight /// @@ -693,13 +709,13 @@ mod pallet { /// Denounce a juror during the voting period for which the commitment vote is known. /// This is useful to punish the behaviour that jurors reveal /// their commitments before the voting period ends. - /// A check of `commitment_hash == hash(juror ++ outcome ++ salt)` is performed for validation. + /// A check of `commitment_hash == hash(juror ++ vote_item ++ salt)` is performed for validation. /// /// # Arguments /// /// - `market_id`: The identifier of the court. /// - `juror`: The juror whose commitment vote might be known. - /// - `outcome`: The raw vote outcome which should match with the commitment of the juror. + /// - `vote_item`: The raw vote item which should match with the commitment of the juror. /// - `salt`: The hash which is used to proof that the juror did reveal /// her vote during the voting period. /// @@ -713,14 +729,16 @@ mod pallet { origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, juror: AccountIdLookupOf, - // TODO: use generic item for votes => court should be a decentralized decision machine - outcome: OutcomeReport, + vote_item: T::VoteItem, salt: T::Hash, ) -> DispatchResultWithPostInfo { let denouncer = ensure_signed(origin)?; let market = T::MarketCommons::market(&market_id)?; - ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); + ensure!( + market.matches_outcome_report(&vote_item.clone().into()), + Error::::OutcomeMismatch + ); let juror = T::Lookup::lookup(juror)?; @@ -742,12 +760,13 @@ mod pallet { let draw = draws[index].clone(); let raw_commmitment = - RawCommitment { juror: juror.clone(), outcome: outcome.clone(), salt }; + RawCommitment { juror: juror.clone(), vote_item: vote_item.clone(), salt }; let commitment = Self::get_hashed_commitment(draw.vote, raw_commmitment)?; // slash for the misbehaviour happens in reassign_juror_stakes - let raw_vote = Vote::Denounced { commitment, outcome: outcome.clone(), salt }; + let raw_vote = + Vote::Denounced { commitment, vote_item: vote_item.clone(), salt }; draws[index] = Draw { vote: raw_vote, ..draw }; } Err(_) => return Err(Error::::JurorNotDrawn.into()), @@ -761,7 +780,7 @@ mod pallet { denouncer, juror, market_id, - outcome, + vote_item, salt, }); @@ -769,12 +788,12 @@ mod pallet { } /// Reveal the commitment vote of the caller juror. - /// A check of `commitment_hash == hash(juror ++ outcome ++ salt)` is performed for validation. + /// A check of `commitment_hash == hash(juror ++ vote_item ++ salt)` is performed for validation. /// /// # Arguments /// /// - `market_id`: The identifier of the court. - /// - `outcome`: The raw vote outcome which should match with the commitment of the juror. + /// - `vote_item`: The raw vote item which should match with the commitment of the juror. /// - `salt`: The hash which is used for the validation. /// /// # Weight @@ -786,14 +805,16 @@ mod pallet { pub fn reveal_vote( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, - // TODO: use generic item for votes => court should be a decentralized decision machine - outcome: OutcomeReport, + vote_item: T::VoteItem, salt: T::Hash, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; let market = T::MarketCommons::market(&market_id)?; - ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); + ensure!( + market.matches_outcome_report(&vote_item.clone().into()), + Error::::OutcomeMismatch + ); ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; @@ -809,11 +830,12 @@ mod pallet { let draw = draws[index].clone(); let raw_commitment = - RawCommitment { juror: who.clone(), outcome: outcome.clone(), salt }; + RawCommitment { juror: who.clone(), vote_item: vote_item.clone(), salt }; let commitment = Self::get_hashed_commitment(draw.vote, raw_commitment)?; - let raw_vote = Vote::Revealed { commitment, outcome: outcome.clone(), salt }; + let raw_vote = + Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }; draws[index] = Draw { juror: who.clone(), vote: raw_vote, ..draw }; } Err(_) => return Err(Error::::JurorNotDrawn.into()), @@ -823,7 +845,12 @@ mod pallet { >::insert(market_id, draws); - Self::deposit_event(Event::JurorRevealedVote { juror: who, market_id, outcome, salt }); + Self::deposit_event(Event::JurorRevealedVote { + juror: who, + market_id, + vote_item, + salt, + }); Ok(Some(T::WeightInfo::reveal_vote(draws_len)).into()) } @@ -860,11 +887,11 @@ mod pallet { let now = >::block_number(); Self::check_appealable_market(&market_id, &court, now)?; - // the outcome which would be resolved on is appealed (including oracle report) + // the vote item which would be resolved on is appealed (including oracle report) let old_draws = SelectedDraws::::get(market_id); - let appealed_outcome = - Self::get_latest_resolved_outcome(&market_id, old_draws.as_slice())?; - let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_outcome }; + let appealed_vote_item = + Self::get_latest_winner_vote_item(&market_id, old_draws.as_slice())?; + let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_vote_item }; court.appeals.try_push(appeal_info).map_err(|_| { debug_assert!(false, "Appeal bound is checked above."); Error::::MaxAppealsReached @@ -949,7 +976,7 @@ mod pallet { T::Currency::resolve_creating(&reward_pot, imbalance); }; - // map delegated jurors to own_slashable, outcome and Vec<(delegator, delegator_stake)> + // map delegated jurors to own_slashable, vote item and Vec<(delegator, delegator_stake)> let mut jurors_to_stakes = BTreeMap::>::new(); for draw in draws { @@ -969,12 +996,12 @@ mod pallet { match draw.vote { Vote::Drawn | Vote::Secret { commitment: _ } - | Vote::Denounced { commitment: _, outcome: _, salt: _ } => { + | Vote::Denounced { commitment: _, vote_item: _, salt: _ } => { slash_juror(&draw.juror, draw.slashable); } - Vote::Revealed { commitment: _, outcome, salt: _ } => { + Vote::Revealed { commitment: _, vote_item, salt: _ } => { jurors_to_stakes.entry(draw.juror).or_default().self_info = - Some(SelfInfo { slashable: draw.slashable, outcome }); + Some(SelfInfo { slashable: draw.slashable, vote_item }); } Vote::Delegated { delegated_stakes } => { let delegator = draw.juror; @@ -1594,7 +1621,7 @@ mod pallet { fn slash_losers_to_award_winners( market_id: &MarketIdOf, jurors_to_stakes: BTreeMap>, - winner_outcome: &OutcomeReport, + winner_vote_item: &T::VoteItem, ) { let mut total_incentives = >::zero(); @@ -1617,8 +1644,8 @@ mod pallet { let mut winners = Vec::<(T::AccountId, BalanceOf)>::new(); for (juror, JurorVoteWithStakes { self_info, delegations }) in jurors_to_stakes.iter() { match self_info { - Some(SelfInfo { slashable, outcome }) => { - if outcome == winner_outcome { + Some(SelfInfo { slashable, vote_item }) => { + if vote_item == winner_vote_item { winners.push((juror.clone(), *slashable)); total_winner_stake = total_winner_stake.saturating_add(*slashable); @@ -1679,19 +1706,19 @@ mod pallet { // Returns the winner of the current court round. // If there is no element inside `draws`, returns `None`. - // If the best two outcomes have the same score, returns the last court round winner. + // If the best two vote items have the same score, returns the last court round winner. pub(crate) fn get_winner( draws: &[DrawOf], - last_winner: Option, - ) -> Option { - let mut scores = BTreeMap::::new(); + last_winner: Option, + ) -> Option { + let mut scores = BTreeMap::::new(); for draw in draws { - if let Vote::Revealed { commitment: _, outcome, salt: _ } = &draw.vote { - if let Some(el) = scores.get_mut(outcome) { + if let Vote::Revealed { commitment: _, vote_item, salt: _ } = &draw.vote { + if let Some(el) = scores.get_mut(vote_item) { *el = el.saturating_add(draw.weight); } else { - scores.insert(outcome.clone(), draw.weight); + scores.insert(vote_item.clone(), draw.weight); } } } @@ -1726,27 +1753,29 @@ mod pallet { Some(best_score.0.clone()) } - // Returns the outcome, on which the market would resolve + // TODO make this independent / generic from the market and oracle outcome. + // Returns the vote item, on which the market would resolve // if the current court round is the final (not appealed) court round. - pub(crate) fn get_latest_resolved_outcome( + pub(crate) fn get_latest_winner_vote_item( market_id: &MarketIdOf, last_draws: &[DrawOf], - ) -> Result { + ) -> Result { let market = T::MarketCommons::market(market_id)?; let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let last_winner: Option = court + let last_winner: Option = court .appeals .last() - .map(|appeal_info| Some(appeal_info.appealed_outcome.clone())) + .map(|appeal_info| Some(appeal_info.appealed_vote_item.clone())) .unwrap_or(None); + // TODO use some generic form to input the default vote item (Query with a trait) let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; let oracle_outcome = report.outcome.clone(); - let resolved_outcome = - Self::get_winner(last_draws, last_winner).unwrap_or(oracle_outcome); - Ok(resolved_outcome) + let winner_vote_item = + Self::get_winner(last_draws, last_winner).unwrap_or_else(|| oracle_outcome.into()); + Ok(winner_vote_item) } - // Check if the (juror, outcome, salt) combination matches the secret hash of the vote. + // Check if the (juror, vote_item, salt) combination matches the secret hash of the vote. pub(crate) fn is_valid(commitment_matcher: CommitmentMatcherOf) -> DispatchResult { // market id and current appeal number is part of salt generation // salt should be signed by the juror (market_id ++ appeal number) @@ -1755,11 +1784,11 @@ mod pallet { // so even if the salt is forgotten it can be reproduced only by the juror let CommitmentMatcher { hashed: commitment, - raw: RawCommitment { juror, outcome, salt }, + raw: RawCommitment { juror, vote_item, salt }, } = commitment_matcher; ensure!( - commitment == T::Hashing::hash_of(&(juror, outcome, salt)), + commitment == T::Hashing::hash_of(&(juror, vote_item, salt)), Error::::InvalidReveal ); @@ -1782,10 +1811,10 @@ mod pallet { } Vote::Drawn => Err(Error::::JurorNotVoted.into()), Vote::Delegated { delegated_stakes: _ } => Err(Error::::JurorDelegated.into()), - Vote::Revealed { commitment: _, outcome: _, salt: _ } => { + Vote::Revealed { commitment: _, vote_item: _, salt: _ } => { Err(Error::::VoteAlreadyRevealed.into()) } - Vote::Denounced { commitment: _, outcome: _, salt: _ } => { + Vote::Denounced { commitment: _, vote_item: _, salt: _ } => { Err(Error::::VoteAlreadyDenounced.into()) } } @@ -1797,31 +1826,31 @@ mod pallet { T: Config, { fn on_dispute_max_weight() -> Weight { - T::WeightInfo::on_dispute_weight() + T::WeightInfo::on_dispute(T::MaxJurors::get(), CacheSize::get()) } fn on_resolution_max_weight() -> Weight { - T::WeightInfo::on_resolution_weight() + T::WeightInfo::on_resolution(T::MaxJurors::get(), T::MaxSelectedDraws::get()) } fn exchange_max_weight() -> Weight { - T::WeightInfo::exchange_weight() + T::WeightInfo::exchange(T::MaxAppeals::get()) } fn get_auto_resolve_max_weight() -> Weight { - T::WeightInfo::get_auto_resolve_weight() + T::WeightInfo::get_auto_resolve() } fn has_failed_max_weight() -> Weight { - T::WeightInfo::has_failed_weight() + T::WeightInfo::has_failed() } fn on_global_dispute_max_weight() -> Weight { - T::WeightInfo::on_global_dispute_weight() + T::WeightInfo::on_global_dispute(T::MaxAppeals::get(), T::MaxSelectedDraws::get()) } fn clear_max_weight() -> Weight { - T::WeightInfo::clear_weight() + T::WeightInfo::clear(T::MaxSelectedDraws::get()) } } @@ -1849,6 +1878,7 @@ mod pallet { ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); let appeal_number = 0usize; + let jurors_len = >::decode_len().unwrap_or(0) as u32; let new_draws = Self::select_jurors(appeal_number)?; let now = >::block_number(); @@ -1864,14 +1894,16 @@ mod pallet { // sets cycle_ends one after the other from now let court = CourtInfo::new(round_timing); - let _ids_len = + let ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.cycle_ends.appeal)?; >::insert(market_id, new_draws); >::insert(market_id, court); - let res = - ResultWithWeightInfo { result: (), weight: T::WeightInfo::on_dispute_weight() }; + let res = ResultWithWeightInfo { + result: (), + weight: T::WeightInfo::on_dispute(jurors_len, ids_len), + }; Ok(res) } @@ -1887,14 +1919,15 @@ mod pallet { let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; let draws = SelectedDraws::::get(market_id); - let resolved_outcome = Self::get_latest_resolved_outcome(market_id, draws.as_slice())?; + let draws_len = draws.len() as u32; + let winner_vote_item = Self::get_latest_winner_vote_item(market_id, draws.as_slice())?; Self::unlock_jurors_from_last_draw(market_id, draws); - court.status = CourtStatus::Closed { winner: resolved_outcome.clone() }; + court.status = CourtStatus::Closed { winner: winner_vote_item.clone() }; >::insert(market_id, court); let res = ResultWithWeightInfo { - result: Some(resolved_outcome), - weight: T::WeightInfo::on_resolution_weight(), + result: Some(winner_vote_item.into()), + weight: T::WeightInfo::on_resolution(T::MaxJurors::get(), draws_len), }; Ok(res) @@ -1912,8 +1945,10 @@ mod pallet { ); let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - for AppealInfo { backer, bond, appealed_outcome } in &court.appeals { - if resolved_outcome == appealed_outcome { + let appeals_len = court.appeals.len() as u32; + for AppealInfo { backer, bond, appealed_vote_item } in &court.appeals { + let appealed_vote_item_as_outcome = appealed_vote_item.clone().into(); + if resolved_outcome == &appealed_vote_item_as_outcome { let (imb, missing) = T::Currency::slash_reserved_named(&Self::reserve_id(), backer, *bond); debug_assert!(missing.is_zero()); @@ -1926,7 +1961,7 @@ mod pallet { // TODO all funds to treasury? let res = ResultWithWeightInfo { result: overall_imbalance, - weight: T::WeightInfo::exchange_weight(), + weight: T::WeightInfo::exchange(appeals_len), }; Ok(res) } @@ -1942,7 +1977,7 @@ mod pallet { let res = ResultWithWeightInfo { result: >::get(market_id).map(|court| court.cycle_ends.appeal), - weight: T::WeightInfo::get_auto_resolve_weight(), + weight: T::WeightInfo::get_auto_resolve(), }; Ok(res) @@ -1989,10 +2024,8 @@ mod pallet { } } - let res = ResultWithWeightInfo { - result: has_failed, - weight: T::WeightInfo::has_failed_weight(), - }; + let res = + ResultWithWeightInfo { result: has_failed, weight: T::WeightInfo::has_failed() }; Ok(res) } @@ -2014,14 +2047,16 @@ mod pallet { let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; let oracle_outcome = &report.outcome; + let appeals_len = court.appeals.len() as u32; + let gd_outcomes = court .appeals .iter() // oracle outcome is added by pm pallet - .filter(|a| &a.appealed_outcome != oracle_outcome) + .filter(|a| &a.appealed_vote_item.clone().into() != oracle_outcome) .map(|a| { GlobalDisputeItem { - outcome: a.appealed_outcome.clone(), + outcome: a.appealed_vote_item.clone().into(), // we have no better global dispute outcome owner owner: Self::treasury_account_id(), // initial vote amount @@ -2031,13 +2066,14 @@ mod pallet { .collect::>>(); let old_draws = SelectedDraws::::get(market_id); + let draws_len = old_draws.len() as u32; Self::unlock_jurors_from_last_draw(market_id, old_draws); >::remove(market_id); >::remove(market_id); let res = ResultWithWeightInfo { result: gd_outcomes, - weight: T::WeightInfo::on_global_dispute_weight(), + weight: T::WeightInfo::on_global_dispute(appeals_len, draws_len), }; Ok(res) @@ -2053,11 +2089,12 @@ mod pallet { ); let old_draws = SelectedDraws::::get(market_id); + let draws_len = old_draws.len() as u32; Self::unlock_jurors_from_last_draw(market_id, old_draws); >::remove(market_id); >::remove(market_id); - let res = ResultWithWeightInfo { result: (), weight: T::WeightInfo::clear_weight() }; + let res = ResultWithWeightInfo { result: (), weight: T::WeightInfo::clear(draws_len) }; Ok(res) } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 669710189..7080e56f1 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -41,7 +41,7 @@ use zeitgeist_primitives::{ traits::DisputeResolutionApi, types::{ AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, - Moment, UncheckedExtrinsicTest, + Moment, UncheckedExtrinsicTest, VoteItem, }, }; @@ -160,6 +160,7 @@ impl crate::Config for Runtime { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; + type VoteItem = VoteItem; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 57e6edf9f..31d632644 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -116,13 +116,14 @@ fn fill_appeals(market_id: &MarketIdOf, appeal_number: usize) { let mut court = Courts::::get(market_id).unwrap(); let mut number = 0u128; while (number as usize) < appeal_number { - let appealed_outcome = OutcomeReport::Scalar(number); + let appealed_vote_item: ::VoteItem = + OutcomeReport::Scalar(number); court .appeals .try_push(AppealInfo { backer: number, bond: crate::get_appeal_bond::(court.appeals.len()), - appealed_outcome, + appealed_vote_item, }) .unwrap(); number += 1; @@ -742,19 +743,23 @@ fn vote_fails_if_court_not_found() { #[test_case( Vote::Revealed { commitment: ::Hash::default(), - outcome: OutcomeReport::Scalar(1u128), + vote_item: OutcomeReport::Scalar(1u128), salt: ::Hash::default(), }; "revealed" )] #[test_case( Vote::Denounced { commitment: ::Hash::default(), - outcome: OutcomeReport::Scalar(1u128), + vote_item: OutcomeReport::Scalar(1u128), salt: ::Hash::default(), }; "denounced" )] fn vote_fails_if_vote_state_incorrect( - vote: crate::Vote<::Hash, crate::DelegatedStakesOf>, + vote: crate::Vote< + ::Hash, + crate::DelegatedStakesOf, + ::VoteItem, + >, ) { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); @@ -865,7 +870,7 @@ fn reveal_vote_works() { assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt,)); System::assert_last_event( - Event::JurorRevealedVote { juror: ALICE, market_id, outcome: outcome.clone(), salt } + Event::JurorRevealedVote { juror: ALICE, market_id, vote_item: outcome.clone(), salt } .into(), ); @@ -880,7 +885,10 @@ fn reveal_vote_works() { assert_eq!(old_draws[alice_index].weight, new_draws[alice_index].weight); assert_eq!(old_draws[alice_index].slashable, new_draws[alice_index].slashable); assert_eq!(old_draws[alice_index].vote, Vote::Secret { commitment }); - assert_eq!(new_draws[alice_index].vote, Vote::Revealed { commitment, outcome, salt }); + assert_eq!( + new_draws[alice_index].vote, + Vote::Revealed { commitment, vote_item: outcome, salt } + ); }); } @@ -1075,7 +1083,7 @@ fn denounce_vote_works() { denouncer: BOB, juror: ALICE, market_id, - outcome: outcome.clone(), + vote_item: outcome.clone(), salt, } .into(), @@ -1088,7 +1096,7 @@ fn denounce_vote_works() { assert_eq!(old_draws[0].weight, new_draws[0].weight); assert_eq!(old_draws[0].slashable, new_draws[0].slashable); assert_eq!(old_draws[0].vote, Vote::Secret { commitment }); - assert_eq!(new_draws[0].vote, Vote::Denounced { commitment, outcome, salt }); + assert_eq!(new_draws[0].vote, Vote::Denounced { commitment, vote_item: outcome, salt }); let free_alice_after = Balances::free_balance(ALICE); let slash = old_draws[0].slashable; @@ -1390,12 +1398,12 @@ fn appeal_get_latest_resolved_outcome_changes() { assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); - let last_appealed_outcome = >::get(market_id) + let last_appealed_vote_item = >::get(market_id) .unwrap() .appeals .last() .unwrap() - .appealed_outcome + .appealed_vote_item .clone(); let request_block = >::get(); @@ -1416,18 +1424,18 @@ fn appeal_get_latest_resolved_outcome_changes() { assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); - let new_appealed_outcome = >::get(market_id) + let new_appealed_vote_item = >::get(market_id) .unwrap() .appeals .last() .unwrap() - .appealed_outcome + .appealed_vote_item .clone(); // if the new appealed outcome were the last appealed outcome, // then the wrong appealed outcome was added in `appeal` - assert_eq!(new_appealed_outcome, outcome); - assert_ne!(last_appealed_outcome, new_appealed_outcome); + assert_eq!(new_appealed_vote_item, outcome); + assert_ne!(last_appealed_vote_item, new_appealed_vote_item); }); } @@ -1562,7 +1570,7 @@ fn appeal_adds_last_appeal() { fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); let last_draws = >::get(market_id); - let appealed_outcome = + let appealed_vote_item = Court::get_latest_resolved_outcome(&market_id, last_draws.as_slice()).unwrap(); assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); @@ -1571,7 +1579,7 @@ fn appeal_adds_last_appeal() { assert!(court.appeals.is_full()); let last_appeal = court.appeals.last().unwrap(); - assert_eq!(last_appeal.appealed_outcome, appealed_outcome); + assert_eq!(last_appeal.appealed_vote_item, appealed_vote_item); }); } @@ -1603,14 +1611,14 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, slashable: 3 * MinJurorStake::get(), }, Draw { juror: DAVE, weight: 1, vote: Vote::Drawn, slashable: 4 * MinJurorStake::get() }, Draw { juror: EVE, weight: 1, - vote: Vote::Denounced { commitment, outcome, salt }, + vote: Vote::Denounced { commitment, vote_item: outcome, salt }, slashable: 5 * MinJurorStake::get(), }, ] @@ -1812,13 +1820,13 @@ fn reassign_juror_stakes_decreases_active_lock() { Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, slashable: charlie_slashable, }, Draw { juror: DAVE, weight: 1, - vote: Vote::Denounced { commitment, outcome, salt }, + vote: Vote::Denounced { commitment, vote_item: outcome, salt }, slashable: dave_slashable, }, ] @@ -1871,25 +1879,25 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, slashable: alice_slashable, }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { commitment, outcome: wrong_outcome_0, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_outcome_0, salt }, slashable: bob_slashable, }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, slashable: charlie_slashable, }, Draw { juror: DAVE, weight: 1, - vote: Vote::Revealed { commitment, outcome: wrong_outcome_1, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_outcome_1, salt }, slashable: dave_slashable, }, ] @@ -1982,19 +1990,19 @@ fn reassign_juror_stakes_works_for_delegations() { Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, slashable: alice_slashable, }, Draw { juror: EVE, weight: 1, - vote: Vote::Revealed { commitment, outcome: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, slashable: eve_slashable, }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { commitment, outcome: wrong_outcome, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_outcome, salt }, slashable: bob_slashable, }, Draw { @@ -2109,25 +2117,25 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { commitment, outcome: wrong_outcome_1.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_outcome_1.clone(), salt }, slashable: MinJurorStake::get(), }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { commitment, outcome: wrong_outcome_0.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_outcome_0.clone(), salt }, slashable: 2 * MinJurorStake::get(), }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, outcome: wrong_outcome_0, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_outcome_0, salt }, slashable: 3 * MinJurorStake::get(), }, Draw { juror: DAVE, weight: 1, - vote: Vote::Revealed { commitment, outcome: wrong_outcome_1, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_outcome_1, salt }, slashable: 4 * MinJurorStake::get(), }, ] @@ -2253,7 +2261,7 @@ fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { let mut slashed_bonds = >::zero(); while (number as usize) < MaxAppeals::get() as usize { let bond = crate::get_appeal_bond::(court.appeals.len()); - let appealed_outcome = if number % 2 == 0 { + let appealed_vote_item = if number % 2 == 0 { // The appeals are not justified, // because the appealed outcomes are equal to the resolved outcome. // it is punished to appeal the right outcome @@ -2268,7 +2276,7 @@ fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { assert_ok!(Balances::reserve_named(&Court::reserve_id(), &backer, bond)); let free_balance = Balances::free_balance(&backer); free_balances_before.insert(backer, free_balance); - court.appeals.try_push(AppealInfo { backer, bond, appealed_outcome }).unwrap(); + court.appeals.try_push(AppealInfo { backer, bond, appealed_vote_item }).unwrap(); number += 1; } Courts::::insert(market_id, court); @@ -2284,12 +2292,12 @@ fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { let court = >::get(market_id).unwrap(); let appeals = court.appeals; - for AppealInfo { backer, bond, appealed_outcome } in appeals { + for AppealInfo { backer, bond, appealed_vote_item } in appeals { assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &backer), 0); let free_balance_after = Balances::free_balance(&backer); let free_balance_before = free_balances_before.get(&backer).unwrap(); - if appealed_outcome == resolved_outcome { + if appealed_vote_item == resolved_outcome { assert_eq!(free_balance_after, *free_balance_before); } else { assert_eq!(free_balance_after, *free_balance_before + bond); @@ -2391,15 +2399,16 @@ fn on_global_dispute_returns_appealed_outcomes() { let initial_vote_amount = >::zero(); let treasury_account = Court::treasury_account_id(); for number in 0..MaxAppeals::get() { - let appealed_outcome = OutcomeReport::Scalar(number as u128); + let appealed_vote_item: ::VoteItem = + OutcomeReport::Scalar(number as u128); let backer = number as u128; let bond = crate::get_appeal_bond::(court.appeals.len()); gd_outcomes.push(GlobalDisputeItem { - outcome: appealed_outcome.clone(), + outcome: appealed_vote_item.clone(), owner: treasury_account, initial_vote_amount, }); - court.appeals.try_push(AppealInfo { backer, bond, appealed_outcome }).unwrap(); + court.appeals.try_push(AppealInfo { backer, bond, appealed_vote_item }).unwrap(); } Courts::::insert(market_id, court); assert_eq!(Court::on_global_dispute(&market_id, &market).unwrap().result, gd_outcomes); @@ -2629,13 +2638,13 @@ fn prepare_draws(market_id: &MarketIdOf, outcomes_with_weights: Vec<(u1 let offset_i = (i + 1000) as u128; let juror = offset_i as u128; let salt = BlakeTwo256::hash_of(&offset_i); - let outcome = OutcomeReport::Scalar(*outcome_index); - let commitment = BlakeTwo256::hash_of(&(juror, outcome.clone(), salt)); + let vote_item: ::VoteItem = OutcomeReport::Scalar(*outcome_index); + let commitment = BlakeTwo256::hash_of(&(juror, vote_item.clone(), salt)); draws .try_push(Draw { juror, weight: *weight, - vote: Vote::Revealed { commitment, outcome, salt }, + vote: Vote::Revealed { commitment, vote_item, salt }, slashable: 0u128, }) .unwrap(); @@ -2681,21 +2690,22 @@ fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { let mut court = >::get(market_id).unwrap(); // create a tie of two best outcomes let weights = vec![(1000u128, 42), (1001u128, 42)]; - let appealed_outcome = OutcomeReport::Scalar(weights.len() as u128); + let appealed_vote_item: ::VoteItem = + OutcomeReport::Scalar(weights.len() as u128); prepare_draws(&market_id, weights); court .appeals .try_push(AppealInfo { backer: CHARLIE, bond: crate::get_appeal_bond::(1usize), - appealed_outcome: appealed_outcome.clone(), + appealed_vote_item: appealed_vote_item.clone(), }) .unwrap(); >::insert(market_id, court); let draws = >::get(market_id); let latest = Court::get_latest_resolved_outcome(&market_id, draws.as_slice()).unwrap(); - assert_eq!(latest, appealed_outcome); + assert_eq!(latest, appealed_vote_item); assert!(latest != ORACLE_REPORT); }); } diff --git a/zrml/court/src/traits.rs b/zrml/court/src/traits.rs new file mode 100644 index 000000000..aa1bb4af2 --- /dev/null +++ b/zrml/court/src/traits.rs @@ -0,0 +1,5 @@ +use frame_support::dispatch::DispatchError; + +pub trait VoteValidation { + fn pre_validate() -> Result<(), DispatchError>; +} \ No newline at end of file diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 059eb0e52..dc45231e2 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -16,7 +16,7 @@ // along with Zeitgeist. If not, see . extern crate alloc; use alloc::{vec, vec::Vec}; -use zeitgeist_primitives::types::OutcomeReport; + /// The general information about a particular juror. #[derive( parity_scale_codec::Decode, @@ -40,21 +40,21 @@ pub struct JurorInfo { } /// The raw information behind the secret hash of a juror's vote. -pub struct RawCommitment { +pub struct RawCommitment { /// The juror's account id. pub juror: AccountId, - /// The outcome which the juror voted for. - pub outcome: OutcomeReport, + /// The vote item which the juror voted for. + pub vote_item: VoteItem, /// The salt which was used to hash the vote. pub salt: Hash, } /// The raw information which is hashed to create the secret hash of a juror's vote. -pub struct CommitmentMatcher { +pub struct CommitmentMatcher { /// The juror's hashed commitment pub hashed: Hash, /// The raw commitment which is intended to lead to the hashed commitment. - pub raw: RawCommitment, + pub raw: RawCommitment, } /// All possible states of a vote. @@ -68,7 +68,7 @@ pub struct CommitmentMatcher { PartialEq, Eq, )] -pub enum Vote { +pub enum Vote { /// The juror delegated stake to other jurors. Delegated { delegated_stakes: DelegatedStakes }, /// The juror was randomly selected to vote in a specific court case. @@ -76,9 +76,9 @@ pub enum Vote { /// The juror casted a vote, only providing a hash, which meaning is unknown. Secret { commitment: Hash }, /// The juror revealed her raw vote, letting anyone know what she voted. - Revealed { commitment: Hash, outcome: OutcomeReport, salt: Hash }, + Revealed { commitment: Hash, vote_item: VoteItem, salt: Hash }, /// The juror was denounced, because she revealed her raw vote during the vote phase. - Denounced { commitment: Hash, outcome: OutcomeReport, salt: Hash }, + Denounced { commitment: Hash, vote_item: VoteItem, salt: Hash }, } /// The information about the lifecycle of a court case. @@ -114,11 +114,11 @@ pub struct CycleEnds { PartialEq, Eq, )] -pub enum CourtStatus { +pub enum CourtStatus { /// The court case has been started. Open, - /// The court case was closed, the winner outcome was determined. - Closed { winner: OutcomeReport }, + /// The court case was closed, the winner vote item was determined. + Closed { winner: VoteItem }, /// The juror stakes from the court were reassigned Reassigned, } @@ -134,13 +134,13 @@ pub enum CourtStatus { PartialEq, Eq, )] -pub struct AppealInfo { +pub struct AppealInfo { /// The account which made the appeal. pub backer: AccountId, /// The amount of funds which were locked for the appeal. pub bond: Balance, - /// The outcome which was appealed. - pub appealed_outcome: OutcomeReport, + /// The vote item which was appealed. + pub appealed_vote_item: VoteItem, } /// The information about a court case. @@ -154,9 +154,9 @@ pub struct AppealInfo { PartialEq, Eq, )] -pub struct CourtInfo { +pub struct CourtInfo { /// The status of the court case. - pub status: CourtStatus, + pub status: CourtStatus, /// The list of all appeals. pub appeals: Appeals, /// The information about the lifecycle of this court case. @@ -175,8 +175,8 @@ pub struct RoundTiming { pub appeal_period: BlockNumber, } -impl - CourtInfo +impl + CourtInfo { pub fn new(round_timing: RoundTiming) -> Self { let pre_vote = round_timing.pre_vote_end; @@ -210,14 +210,14 @@ impl PartialEq, Eq, )] -pub struct Draw { +pub struct Draw { /// The juror who was randomly selected. pub juror: AccountId, /// The weight of the juror in this court case. /// The higher the weight the more voice the juror has in the final winner decision. pub weight: u32, /// The information about the vote state. - pub vote: Vote, + pub vote: Vote, /// The amount of funds which can be slashed for this court case. /// This is related to a multiple of `MinStake` to mitigate Sybil attacks. pub slashable: Balance, @@ -276,13 +276,13 @@ pub enum SelectionAdd { DelegationWeight, } -pub struct SelfInfo { +pub struct SelfInfo { pub slashable: Balance, - pub outcome: OutcomeReport, + pub vote_item: VoteItem, } -pub struct JurorVoteWithStakes { - pub self_info: Option>, +pub struct JurorVoteWithStakes { + pub self_info: Option>, // many delegators can have delegated to the same juror // that's why the value is a vector and should be sorted (binary search by key) // the key is the delegator account @@ -290,7 +290,7 @@ pub struct JurorVoteWithStakes { pub delegations: Vec<(AccountId, Balance)>, } -impl Default for JurorVoteWithStakes { +impl Default for JurorVoteWithStakes { fn default() -> Self { JurorVoteWithStakes { self_info: None, delegations: vec![] } } diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index c7a1cc445..89ce294dd 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Zeitgeist. If not, see . - //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev @@ -46,26 +45,26 @@ use frame_support::{traits::Get, weights::Weight}; /// Trait containing the required functions for weight retrival within /// zrml_court (automatically generated) pub trait WeightInfoZeitgeist { - fn join_court(j: u32, ) -> Weight; - fn delegate(j: u32, d: u32, ) -> Weight; - fn prepare_exit_court(j: u32, ) -> Weight; + fn join_court(j: u32) -> Weight; + fn delegate(j: u32, d: u32) -> Weight; + fn prepare_exit_court(j: u32) -> Weight; fn exit_court_remove() -> Weight; fn exit_court_set() -> Weight; - fn vote(d: u32, ) -> Weight; - fn denounce_vote(d: u32, ) -> Weight; - fn reveal_vote(d: u32, ) -> Weight; - fn appeal(j: u32, a: u32, r: u32, f: u32, ) -> Weight; - fn reassign_juror_stakes(d: u32, ) -> Weight; + fn vote(d: u32) -> Weight; + fn denounce_vote(d: u32) -> Weight; + fn reveal_vote(d: u32) -> Weight; + fn appeal(j: u32, a: u32, r: u32, f: u32) -> Weight; + fn reassign_juror_stakes(d: u32) -> Weight; fn set_inflation() -> Weight; - fn handle_inflation(j: u32, ) -> Weight; - fn select_jurors(a: u32, ) -> Weight; - fn on_dispute(j: u32, r: u32, ) -> Weight; - fn on_resolution(j: u32, d: u32, ) -> Weight; - fn exchange(a: u32, ) -> Weight; + fn handle_inflation(j: u32) -> Weight; + fn select_jurors(a: u32) -> Weight; + fn on_dispute(j: u32, r: u32) -> Weight; + fn on_resolution(j: u32, d: u32) -> Weight; + fn exchange(a: u32) -> Weight; fn get_auto_resolve() -> Weight; fn has_failed() -> Weight; - fn on_global_dispute(a: u32, d: u32, ) -> Weight; - fn clear(d: u32, ) -> Weight; + fn on_global_dispute(a: u32, d: u32) -> Weight; + fn clear(d: u32) -> Weight; } /// Weight functions for zrml_court (automatically generated) @@ -74,7 +73,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn join_court(j: u32, ) -> Weight { + fn join_court(j: u32) -> Weight { Weight::from_ref_time(33_635_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(98_000).saturating_mul(j.into())) @@ -84,7 +83,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court Jurors (r:6 w:1) // Storage: Balances Locks (r:1 w:1) - fn delegate(j: u32, d: u32, ) -> Weight { + fn delegate(j: u32, d: u32) -> Weight { Weight::from_ref_time(48_104_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(122_000).saturating_mul(j.into())) @@ -96,7 +95,7 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court Jurors (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) - fn prepare_exit_court(j: u32, ) -> Weight { + fn prepare_exit_court(j: u32) -> Weight { Weight::from_ref_time(19_274_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(84_000).saturating_mul(j.into())) @@ -119,7 +118,7 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn vote(d: u32, ) -> Weight { + fn vote(d: u32) -> Weight { Weight::from_ref_time(49_081_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(106_000).saturating_mul(d.into())) @@ -130,7 +129,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn denounce_vote(d: u32, ) -> Weight { + fn denounce_vote(d: u32) -> Weight { Weight::from_ref_time(39_078_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(124_000).saturating_mul(d.into())) @@ -141,7 +140,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn reveal_vote(d: u32, ) -> Weight { + fn reveal_vote(d: u32) -> Weight { Weight::from_ref_time(66_728_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(91_000).saturating_mul(d.into())) @@ -158,7 +157,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Balances Reserves (r:1 w:1) - fn appeal(j: u32, a: u32, _r: u32, _f: u32, ) -> Weight { + fn appeal(j: u32, a: u32, _r: u32, _f: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 3_000 .saturating_add(Weight::from_ref_time(1_390_000).saturating_mul(j.into())) @@ -173,7 +172,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:5 w:5) // Storage: System Account (r:6 w:5) - fn reassign_juror_stakes(d: u32, ) -> Weight { + fn reassign_juror_stakes(d: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 8_000 .saturating_add(Weight::from_ref_time(41_797_000).saturating_mul(d.into())) @@ -184,13 +183,12 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court YearlyInflation (r:0 w:1) fn set_inflation() -> Weight { - Weight::from_ref_time(6_000_000) - .saturating_add(T::DbWeight::get().writes(1)) + Weight::from_ref_time(6_000_000).saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court YearlyInflation (r:1 w:0) // Storage: Court JurorPool (r:1 w:0) // Storage: System Account (r:1 w:1) - fn handle_inflation(j: u32, ) -> Weight { + fn handle_inflation(j: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 25_000 .saturating_add(Weight::from_ref_time(14_531_000).saturating_mul(j.into())) @@ -202,7 +200,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) // Storage: Court Jurors (r:9 w:5) - fn select_jurors(a: u32, ) -> Weight { + fn select_jurors(a: u32) -> Weight { Weight::from_ref_time(226_140_000) // Standard Error: 2_247_000 .saturating_add(Weight::from_ref_time(507_706_000).saturating_mul(a.into())) @@ -219,7 +217,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) // Storage: Court SelectedDraws (r:0 w:1) - fn on_dispute(j: u32, r: u32, ) -> Weight { + fn on_dispute(j: u32, r: u32) -> Weight { Weight::from_ref_time(65_336_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(164_000).saturating_mul(j.into())) @@ -232,7 +230,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectedDraws (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court Jurors (r:94 w:94) - fn on_resolution(j: u32, d: u32, ) -> Weight { + fn on_resolution(j: u32, d: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 0 .saturating_add(Weight::from_ref_time(99_000).saturating_mul(j.into())) @@ -246,7 +244,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Courts (r:1 w:0) // Storage: Balances Reserves (r:1 w:1) // Storage: System Account (r:1 w:1) - fn exchange(a: u32, ) -> Weight { + fn exchange(a: u32) -> Weight { Weight::from_ref_time(13_802_000) // Standard Error: 30_000 .saturating_add(Weight::from_ref_time(21_667_000).saturating_mul(a.into())) @@ -256,20 +254,18 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court Courts (r:1 w:0) fn get_auto_resolve() -> Weight { - Weight::from_ref_time(7_000_000) - .saturating_add(T::DbWeight::get().reads(1)) + Weight::from_ref_time(7_000_000).saturating_add(T::DbWeight::get().reads(1)) } // Storage: Court JurorPool (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) fn has_failed() -> Weight { - Weight::from_ref_time(17_000_000) - .saturating_add(T::DbWeight::get().reads(3)) + Weight::from_ref_time(17_000_000).saturating_add(T::DbWeight::get().reads(3)) } // Storage: Court Courts (r:1 w:1) // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:94 w:94) - fn on_global_dispute(_a: u32, d: u32, ) -> Weight { + fn on_global_dispute(_a: u32, d: u32) -> Weight { Weight::from_ref_time(12_713_000) // Standard Error: 1_000 .saturating_add(Weight::from_ref_time(4_069_000).saturating_mul(d.into())) @@ -281,7 +277,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:1 w:1) // Storage: Court Courts (r:0 w:1) - fn clear(d: u32, ) -> Weight { + fn clear(d: u32) -> Weight { Weight::from_ref_time(9_761_000) // Standard Error: 1_000 .saturating_add(Weight::from_ref_time(4_033_000).saturating_mul(d.into())) diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index ddd3372b9..aba85eef2 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -53,6 +53,7 @@ use zeitgeist_primitives::{ types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, CurrencyId, Hash, Index, MarketId, Moment, PoolId, SerdeWrapper, UncheckedExtrinsicTest, + VoteItem, }, }; @@ -270,6 +271,7 @@ impl zrml_court::Config for Runtime { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; + type VoteItem = VoteItem; type WeightInfo = zrml_court::weights::WeightInfo; } From 0b0de97a74e04541c8221ff75f640533b647486d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 26 Apr 2023 14:28:20 +0200 Subject: [PATCH 275/368] update to court id and generic court --- primitives/src/types.rs | 2 - runtime/common/src/lib.rs | 51 +- zrml/court/src/benchmarks.rs | 156 +++--- zrml/court/src/lib.rs | 402 +++++++++------- zrml/court/src/mock.rs | 59 ++- zrml/court/src/tests.rs | 694 +++++++++++++++------------ zrml/court/src/traits.rs | 22 +- zrml/court/src/types.rs | 91 +++- zrml/prediction-markets/src/mock.rs | 59 ++- zrml/prediction-markets/src/tests.rs | 45 +- 10 files changed, 967 insertions(+), 614 deletions(-) diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 52467c3d7..a380cf656 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -168,5 +168,3 @@ pub struct XcmMetadata { /// Should be updated regularly. pub fee_factor: Option, } - -pub type VoteItem = OutcomeReport; diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 11c31b871..4a42986fa 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -931,8 +931,56 @@ macro_rules! impl_config_traits { type WeightInfo = zrml_authorized::weights::WeightInfo; } + use zrml_court::Error as CourtError; + use zrml_court::types::VoteItem; + use zrml_court::traits::{VoteCheckApi, AppealCheckApi, DefaultWinnerApi}; + use frame_support::ensure; + use zrml_market_commons::MarketCommonsPalletApi; + + pub struct AppealCheck; + impl AppealCheckApi for AppealCheck { + type MarketId = MarketId; + + fn pre_appeal(market_id: &Self::MarketId) -> Result<(), sp_runtime::DispatchError> { + let market = MarketCommons::market(market_id)?; + ensure!(market.status == MarketStatus::Disputed, CourtError::::MarketIsNotDisputed); + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + CourtError::::MarketDoesNotHaveCourtMechanism + ); + Ok(()) + } + } + + pub struct VoteCheck; + impl VoteCheckApi for VoteCheck { + type MarketId = MarketId; + + fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), sp_runtime::DispatchError> { + let market = MarketCommons::market(&market_id)?; + ensure!( + market.matches_outcome_report(&vote_item.clone().into_outcome().unwrap()), + CourtError::::OutcomeMismatch + ); + Ok(()) + } + } + + pub struct DefaultWinner; + impl DefaultWinnerApi for DefaultWinner { + type MarketId = MarketId; + + fn default_winner(market_id: &Self::MarketId) -> Result { + let market = MarketCommons::market(market_id)?; + let report = market.report.as_ref().ok_or(CourtError::::MarketReportNotFound)?; + let vote_item = VoteItem::Outcome(report.outcome.clone()); + Ok(vote_item) + } + } + impl zrml_court::Config for Runtime { type AppealBond = AppealBond; + type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; @@ -940,6 +988,7 @@ macro_rules! impl_config_traits { type CourtLockId = CourtLockId; type CourtPalletId = CourtPalletId; type Currency = Balances; + type DefaultWinner = DefaultWinner; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type InflationPeriod = InflationPeriod; @@ -954,7 +1003,7 @@ macro_rules! impl_config_traits { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; - type VoteItem = zeitgeist_primitives::types::VoteItem; + type VoteCheck = VoteCheck; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index c0bbe5207..be044cc97 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -25,8 +25,8 @@ extern crate alloc; use crate::{ types::{CourtStatus, Draw, JurorInfo, JurorPoolItem, Vote}, - AppealInfo, BalanceOf, Call, Config, Courts, DelegatedStakesOf, JurorPool, Jurors, MarketOf, - Pallet as Court, Pallet, RequestBlock, SelectedDraws, + AppealInfo, BalanceOf, Call, Config, CourtId, Courts, DelegatedStakesOf, JurorPool, Jurors, + MarketIdToCourtId, MarketOf, Pallet as Court, Pallet, RequestBlock, SelectedDraws, VoteItem, }; use alloc::{vec, vec::Vec}; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; @@ -153,7 +153,7 @@ where Ok(()) } -fn setup_court() -> Result, &'static str> +fn setup_court() -> Result<(crate::MarketIdOf, CourtId), &'static str> where T: Config, { @@ -165,16 +165,18 @@ where let market_id = T::MarketCommons::push_market(get_market::()).unwrap(); Court::::on_dispute(&market_id, &get_market::()).unwrap(); - Ok(market_id) + let court_id = >::get(market_id).unwrap(); + + Ok((market_id, court_id)) } -fn fill_draws(market_id: crate::MarketIdOf, number: u32) -> Result<(), &'static str> +fn fill_draws(court_id: CourtId, number: u32) -> Result<(), &'static str> where T: Config, { // remove last random selections of on_dispute - >::remove(market_id); - let mut draws = >::get(market_id); + >::remove(court_id); + let mut draws = >::get(court_id); for i in 0..number { let juror = account("juror", i, 0); deposit::(&juror); @@ -194,23 +196,27 @@ where .unwrap_or_else(|j| j); draws.try_insert(index, draw).unwrap(); } - >::insert(market_id, draws); + >::insert(court_id, draws); Ok(()) } -fn apply_revealed_draws(market_id: crate::MarketIdOf) +fn apply_revealed_draws(court_id: CourtId) where T: Config, { let winner_outcome = OutcomeReport::Scalar(0u128); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); // change draws to have revealed votes for draw in draws.iter_mut() { let salt = Default::default(); let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); - draw.vote = Vote::Revealed { commitment, vote_item: winner_outcome.clone().into(), salt }; + draw.vote = Vote::Revealed { + commitment, + vote_item: VoteItem::Outcome(winner_outcome.clone()), + salt, + }; } - >::insert(market_id, draws); + >::insert(court_id, draws); } benchmarks! { @@ -292,14 +298,14 @@ benchmarks! { fill_pool::(T::MaxJurors::get() - 1)?; let caller: T::AccountId = whitelisted_caller(); - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let pre_vote = court.cycle_ends.pre_vote; - fill_draws::(market_id, d)?; + fill_draws::(court_id, d)?; - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); let draws_len = draws.len(); draws.remove(0); let draw = Draw { @@ -310,12 +316,12 @@ benchmarks! { }; let index = draws.binary_search_by_key(&caller, |draw| draw.juror.clone()).unwrap_or_else(|j| j); draws.try_insert(index, draw).unwrap(); - >::insert(market_id, draws); + >::insert(court_id, draws); >::set_block_number(pre_vote + 1u64.saturated_into::()); let commitment_vote = Default::default(); - }: _(RawOrigin::Signed(caller), market_id, commitment_vote) + }: _(RawOrigin::Signed(caller), court_id, commitment_vote) denounce_vote { let d in 1..T::MaxSelectedDraws::get(); @@ -324,15 +330,16 @@ benchmarks! { fill_pool::(necessary_jurors_weight as u32)?; let caller: T::AccountId = whitelisted_caller(); - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let pre_vote = court.cycle_ends.pre_vote; - fill_draws::(market_id, d)?; + fill_draws::(court_id, d)?; let salt = Default::default(); let outcome = OutcomeReport::Scalar(0u128); + let vote_item = VoteItem::Outcome(outcome); let denounced_juror: T::AccountId = account("denounced_juror", 0, 0); join_with_min_stake::(&denounced_juror)?; >::insert(&denounced_juror, JurorInfo { @@ -342,9 +349,9 @@ benchmarks! { delegations: Default::default(), }); let denounced_juror_unlookup = T::Lookup::unlookup(denounced_juror.clone()); - let commitment = T::Hashing::hash_of(&(denounced_juror.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(denounced_juror.clone(), vote_item.clone(), salt)); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); draws.remove(0); let draws_len = draws.len(); let index = draws.binary_search_by_key(&denounced_juror, |draw| draw.juror.clone()).unwrap_or_else(|j| j); @@ -355,10 +362,10 @@ benchmarks! { slashable: T::MinJurorStake::get(), }; draws.try_insert(index, draw).unwrap(); - >::insert(market_id, draws); + >::insert(court_id, draws); >::set_block_number(pre_vote + 1u64.saturated_into::()); - }: _(RawOrigin::Signed(caller), market_id, denounced_juror_unlookup, outcome.into(), salt) + }: _(RawOrigin::Signed(caller), court_id, denounced_juror_unlookup, vote_item, salt) reveal_vote { let d in 1..T::MaxSelectedDraws::get(); @@ -366,15 +373,16 @@ benchmarks! { fill_pool::(T::MaxJurors::get() - 1)?; let caller: T::AccountId = whitelisted_caller(); - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let vote_end = court.cycle_ends.vote; - fill_draws::(market_id, d)?; + fill_draws::(court_id, d)?; let salt = Default::default(); let outcome = OutcomeReport::Scalar(0u128); + let vote_item = VoteItem::Outcome(outcome); join_with_min_stake::(&caller)?; >::insert(&caller, JurorInfo { stake: T::MinJurorStake::get(), @@ -382,9 +390,9 @@ benchmarks! { prepare_exit_at: None, delegations: Default::default(), }); - let commitment = T::Hashing::hash_of(&(caller.clone(), outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(caller.clone(), vote_item.clone(), salt)); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); let draws_len = draws.len(); draws.remove(0); let index = draws.binary_search_by_key(&caller, |draw| draw.juror.clone()).unwrap_or_else(|j| j); @@ -394,10 +402,10 @@ benchmarks! { weight: 1u32, slashable: T::MinJurorStake::get(), }).unwrap(); - >::insert(market_id, draws); + >::insert(court_id, draws); >::set_block_number(vote_end + 1u64.saturated_into::()); - }: _(RawOrigin::Signed(caller), market_id, outcome.into(), salt) + }: _(RawOrigin::Signed(caller), court_id, vote_item, salt) appeal { // from 47 because in the last appeal round we need at least 47 jurors @@ -413,9 +421,9 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); deposit::(&caller); - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; - let mut court = >::get(market_id).unwrap(); + let mut court = >::get(court_id).unwrap(); let appeal_end = court.cycle_ends.appeal; for i in 0..r { let market_id_i = (i + 100).saturated_into::>(); @@ -428,16 +436,16 @@ benchmarks! { let appeal_info = AppealInfo { backer: account("backer", i, 0), bond: crate::get_appeal_bond::(i as usize), - appealed_vote_item: OutcomeReport::Scalar(0u128).into(), + appealed_vote_item: VoteItem::Outcome(OutcomeReport::Scalar(0u128)), }; court.appeals.try_push(appeal_info).unwrap(); } - >::insert(market_id, court); + >::insert(court_id, court); let salt = Default::default(); // remove last random selections of on_dispute - >::remove(market_id); - let mut draws = >::get(market_id); + >::remove(court_id); + let mut draws = >::get(court_id); let draws_len = Court::::necessary_jurors_weight(a as usize) as u32; for i in 0..draws_len { let juror: T::AccountId = account("juror", i, 0); @@ -447,7 +455,7 @@ benchmarks! { prepare_exit_at: None, delegations: Default::default(), }); - let vote_item: T::VoteItem = OutcomeReport::Scalar(i as u128).into(); + let vote_item: VoteItem = VoteItem::Outcome(OutcomeReport::Scalar(i as u128)); let commitment = T::Hashing::hash_of(&(juror.clone(), vote_item.clone(), salt)); let draw = Draw { @@ -458,7 +466,7 @@ benchmarks! { }; draws.try_push(draw).unwrap(); } - >::insert(market_id, draws); + >::insert(court_id, draws); >::set_block_number(aggregation + 1u64.saturated_into::()); let now = >::block_number(); @@ -472,9 +480,9 @@ benchmarks! { let market_id_i = (i + 100).saturated_into::>(); T::DisputeResolution::add_auto_resolve(&market_id_i, new_resolve_at).unwrap(); } - }: _(RawOrigin::Signed(caller), market_id) + }: _(RawOrigin::Signed(caller), court_id) verify { - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); assert_eq!(court.cycle_ends.appeal, new_resolve_at); } @@ -487,18 +495,20 @@ benchmarks! { fill_pool::(necessary_jurors_weight as u32)?; let caller: T::AccountId = whitelisted_caller(); - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; - let mut court = >::get(market_id).unwrap(); + let mut court = >::get(court_id).unwrap(); let winner_outcome = OutcomeReport::Scalar(0u128); let wrong_outcome = OutcomeReport::Scalar(1u128); - court.status = CourtStatus::Closed { winner: winner_outcome.clone().into() }; - >::insert(market_id, court); + let winner_vote_item = VoteItem::Outcome(winner_outcome); + let wrong_vote_item = VoteItem::Outcome(wrong_outcome); + court.status = CourtStatus::Closed { winner: winner_vote_item.clone() }; + >::insert(court_id, court); let salt = Default::default(); // remove last random selections of on_dispute - >::remove(market_id); - let mut draws = >::get(market_id); + >::remove(court_id); + let mut draws = >::get(court_id); let mut delegated_stakes: DelegatedStakesOf = Default::default(); for i in 0..d { let juror: T::AccountId = account("juror", i, 0); @@ -512,10 +522,10 @@ benchmarks! { let draw = if i < T::MaxDelegations::get() { delegated_stakes.try_push((juror.clone(), T::MinJurorStake::get())).unwrap(); - let vote_item: T::VoteItem = if i % 2 == 0 { - wrong_outcome.clone().into() + let vote_item: VoteItem = if i % 2 == 0 { + wrong_vote_item.clone() } else { - winner_outcome.clone().into() + winner_vote_item.clone() }; let commitment = T::Hashing::hash_of(&(juror.clone(), vote_item.clone(), salt)); Draw { @@ -534,8 +544,8 @@ benchmarks! { }; draws.try_push(draw).unwrap(); } - >::insert(market_id, draws); - }: _(RawOrigin::Signed(caller), market_id) + >::insert(court_id, draws); + }: _(RawOrigin::Signed(caller), court_id) set_inflation { let inflation = Perbill::from_percent(10); @@ -592,24 +602,24 @@ benchmarks! { fill_pool::(5)?; - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; let market = get_market::(); - fill_draws::(market_id, d)?; + fill_draws::(court_id, d)?; let winner_outcome = OutcomeReport::Scalar(0u128); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); // change draws to have revealed votes for draw in draws.iter_mut() { let salt = Default::default(); let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); draw.vote = Vote::Revealed { commitment, - vote_item: winner_outcome.clone().into(), + vote_item: VoteItem::Outcome(winner_outcome.clone()), salt, }; } - >::insert(market_id, draws); + >::insert(court_id, draws); }: { Court::::on_resolution(&market_id, &market).unwrap(); } @@ -618,10 +628,10 @@ benchmarks! { let a in 0..T::MaxAppeals::get(); fill_pool::(5)?; - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; let market = get_market::(); - let mut court = >::get(market_id).unwrap(); + let mut court = >::get(court_id).unwrap(); let resolved_outcome = OutcomeReport::Scalar(0u128); for i in 0..a { @@ -632,18 +642,18 @@ benchmarks! { let appeal_info = AppealInfo { backer, bond, - appealed_vote_item: resolved_outcome.clone().into(), + appealed_vote_item: VoteItem::Outcome(resolved_outcome.clone()), }; court.appeals.try_push(appeal_info).unwrap(); } - >::insert(market_id, court); + >::insert(court_id, court); }: { Court::::exchange(&market_id, &market, &resolved_outcome, Default::default()).unwrap(); } get_auto_resolve { fill_pool::(5)?; - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; let market = get_market::(); }: { Court::::get_auto_resolve(&market_id, &market).unwrap(); @@ -651,7 +661,7 @@ benchmarks! { has_failed { fill_pool::(5)?; - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; let market = get_market::(); }: { Court::::has_failed(&market_id, &market).unwrap(); @@ -662,15 +672,15 @@ benchmarks! { let d in 1..T::MaxSelectedDraws::get(); fill_pool::(5)?; - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; let market = get_market::(); - fill_draws::(market_id, d)?; - apply_revealed_draws::(market_id); + fill_draws::(court_id, d)?; + apply_revealed_draws::(court_id); let resolved_outcome = OutcomeReport::Scalar(0u128); - let mut court = >::get(market_id).unwrap(); + let mut court = >::get(court_id).unwrap(); for i in 0..a { let backer = account("backer", i, 0); let bond = T::MinJurorStake::get(); @@ -679,11 +689,11 @@ benchmarks! { let appeal_info = AppealInfo { backer, bond, - appealed_vote_item: resolved_outcome.clone().into(), + appealed_vote_item: VoteItem::Outcome(resolved_outcome.clone()), }; court.appeals.try_push(appeal_info).unwrap(); } - >::insert(market_id, court); + >::insert(court_id, court); }: { Court::::on_global_dispute(&market_id, &market).unwrap(); } @@ -693,11 +703,11 @@ benchmarks! { fill_pool::(5)?; - let market_id = setup_court::()?; + let (market_id, court_id) = setup_court::()?; let market = get_market::(); - fill_draws::(market_id, d)?; - apply_revealed_draws::(market_id); + fill_draws::(court_id, d)?; + apply_revealed_draws::(court_id); }: { Court::::clear(&market_id, &market).unwrap(); } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 59cefdfb7..4ea55dbe2 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -23,9 +23,11 @@ extern crate alloc; use crate::{ - weights::WeightInfoZeitgeist, AppealInfo, CommitmentMatcher, CourtInfo, CourtStatus, Draw, - JurorInfo, JurorPoolItem, JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, - SelectionError, SelectionValue, SelfInfo, Vote, + traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, + weights::WeightInfoZeitgeist, + AppealInfo, CommitmentMatcher, CourtId, CourtInfo, CourtStatus, CourtVoteItem, Draw, JurorInfo, + JurorPoolItem, JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, SelectionError, + SelectionValue, SelfInfo, Vote, VoteItem, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -36,15 +38,15 @@ use frame_support::{ dispatch::DispatchResult, ensure, log, pallet_prelude::{ - ConstU32, DispatchResultWithPostInfo, EnsureOrigin, Hooks, MaxEncodedLen, Member, - OptionQuery, StorageMap, StorageValue, ValueQuery, Weight, + ConstU32, DispatchResultWithPostInfo, EnsureOrigin, Hooks, OptionQuery, StorageMap, + StorageValue, ValueQuery, Weight, }, traits::{ Currency, Get, Imbalance, IsType, LockIdentifier, LockableCurrency, NamedReservableCurrency, OnUnbalanced, Randomness, ReservableCurrency, StorageVersion, WithdrawReasons, }, - transactional, Blake2_128Concat, BoundedVec, PalletId, Parameter, + transactional, Blake2_128Concat, BoundedVec, PalletId, }; use frame_system::{ ensure_signed, @@ -60,7 +62,7 @@ use sp_runtime::{ use zeitgeist_primitives::{ traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}, types::{ - Asset, GlobalDisputeItem, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport, + Asset, GlobalDisputeItem, Market, MarketDisputeMechanism, OutcomeReport, ResultWithWeightInfo, }, }; @@ -72,6 +74,7 @@ pub mod migrations; mod mock; mod mock_storage; mod tests; +pub mod traits; pub mod types; pub mod weights; @@ -81,6 +84,8 @@ pub use types::*; #[frame_support::pallet] mod pallet { + use frame_support::Twox64Concat; + use super::*; #[pallet::config] @@ -90,6 +95,8 @@ mod pallet { #[pallet::constant] type AppealBond: Get>; + type AppealCheck: AppealCheckApi>; + #[pallet::constant] type BlocksPerYear: Get; @@ -118,6 +125,8 @@ mod pallet { + NamedReservableCurrency + LockableCurrency; + type DefaultWinner: DefaultWinnerApi>; + /// The functionality to allow controlling the markets resolution time. type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, @@ -175,16 +184,7 @@ mod pallet { #[pallet::constant] type TreasuryPalletId: Get; - /// The vote item to be used for the court. - type VoteItem: Parameter - + Member - + Clone - + Ord - + PartialEq - + Eq - + MaxEncodedLen - + Into - + From; + type VoteCheck: VoteCheckApi>; /// Weights generated by benchmarks type WeightInfo: WeightInfoZeitgeist; @@ -217,32 +217,22 @@ mod pallet { pub(crate) type HashOf = ::Hash; pub(crate) type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - pub(crate) type CourtOf = - CourtInfo<::BlockNumber, AppealsOf, ::VoteItem>; + pub(crate) type CourtOf = CourtInfo<::BlockNumber, AppealsOf>; pub(crate) type DelegatedStakesOf = BoundedVec<(AccountIdOf, BalanceOf), ::MaxDelegations>; pub(crate) type SelectionValueOf = SelectionValue, DelegatedStakesOf>; pub(crate) type DelegationsOf = BoundedVec, ::MaxDelegations>; - pub(crate) type VoteOf = Vote, DelegatedStakesOf, ::VoteItem>; - pub(crate) type JurorVoteWithStakesOf = - JurorVoteWithStakes, BalanceOf, ::VoteItem>; + pub(crate) type VoteOf = Vote, DelegatedStakesOf>; + pub(crate) type JurorVoteWithStakesOf = JurorVoteWithStakes, BalanceOf>; pub(crate) type JurorInfoOf = JurorInfo, BlockNumberFor, DelegationsOf>; pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; - pub(crate) type DrawOf = Draw< - AccountIdOf, - BalanceOf, - HashOf, - DelegatedStakesOf, - ::VoteItem, - >; + pub(crate) type DrawOf = Draw, BalanceOf, HashOf, DelegatedStakesOf>; pub(crate) type SelectedDrawsOf = BoundedVec, ::MaxSelectedDraws>; - pub(crate) type AppealOf = AppealInfo, BalanceOf, ::VoteItem>; + pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; - pub(crate) type CommitmentMatcherOf = - CommitmentMatcher, HashOf, ::VoteItem>; - pub(crate) type RawCommitmentOf = - RawCommitment, HashOf, ::VoteItem>; + pub(crate) type CommitmentMatcherOf = CommitmentMatcher, HashOf>; + pub(crate) type RawCommitmentOf = RawCommitment, HashOf>; pub type CacheSize = ConstU32<64>; #[pallet::pallet] @@ -263,18 +253,25 @@ mod pallet { #[pallet::storage] pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; - // TODO make everything independent from the concept of market ids. - // TODO so that the integration of this pallet elsewhere is easier - /// The randomly selected jurors with their vote. #[pallet::storage] pub type SelectedDraws = - StorageMap<_, Blake2_128Concat, MarketIdOf, SelectedDrawsOf, ValueQuery>; + StorageMap<_, Blake2_128Concat, CourtId, SelectedDrawsOf, ValueQuery>; /// The general information about each court. #[pallet::storage] - pub type Courts = - StorageMap<_, Blake2_128Concat, MarketIdOf, CourtOf, OptionQuery>; + pub type Courts = StorageMap<_, Blake2_128Concat, CourtId, CourtOf, OptionQuery>; + + #[pallet::storage] + pub type NextCourtId = StorageValue<_, CourtId, ValueQuery>; + + #[pallet::storage] + pub type MarketIdToCourtId = + StorageMap<_, Twox64Concat, MarketIdOf, CourtId, OptionQuery>; + + #[pallet::storage] + pub type CourtIdToMarketId = + StorageMap<_, Twox64Concat, CourtId, MarketIdOf, OptionQuery>; /// The block number in the future when jurors should start voting. /// This is useful for the user experience of the jurors to vote for multiple courts at once. @@ -314,23 +311,23 @@ mod pallet { }, /// A juror has voted in a court. JurorVoted { - market_id: MarketIdOf, + court_id: CourtId, juror: T::AccountId, commitment: T::Hash, }, /// A juror has revealed their vote. JurorRevealedVote { juror: T::AccountId, - market_id: MarketIdOf, - vote_item: T::VoteItem, + court_id: CourtId, + vote_item: VoteItem, salt: T::Hash, }, /// A juror vote has been denounced. DenouncedJurorVote { denouncer: T::AccountId, juror: T::AccountId, - market_id: MarketIdOf, - vote_item: T::VoteItem, + court_id: CourtId, + vote_item: VoteItem, salt: T::Hash, }, /// A delegator has delegated their stake to jurors. @@ -340,8 +337,8 @@ mod pallet { delegated_jurors: Vec, }, /// A market has been appealed. - MarketAppealed { - market_id: MarketIdOf, + CourtAppealed { + court_id: CourtId, appeal_number: u32, }, MintedInCourt { @@ -353,7 +350,7 @@ mod pallet { /// The losing jurors are those, who did not vote, /// were denounced or did not reveal their vote. JurorStakesReassigned { - market_id: MarketIdOf, + court_id: CourtId, }, } @@ -381,8 +378,6 @@ mod pallet { MaxAppealsReached, /// This operation is only allowed in the appeal period. NotInAppealPeriod, - /// The court is already present for this market. - CourtAlreadyExists, /// The caller of this extrinsic needs to be drawn or in the commitment vote state. InvalidVoteState, /// The amount is below the minimum required stake. @@ -410,6 +405,8 @@ mod pallet { NotEnoughJurors, /// The report of the market was not found. MarketReportNotFound, + /// The maximum number of court ids is reached. + MaxCourtIdReached, /// The caller has not enough funds to join the court with the specified amount. AmountExceedsBalance, /// After the first join of the court the amount has to be higher than the current stake. @@ -424,8 +421,6 @@ mod pallet { CallerNotInSelectedDraws, /// The callers balance is lower than the appeal bond. AppealBondExceedsBalance, - /// The outcome does not match the market outcome type. - OutcomeMismatch, /// The juror should at least wait one inflation period after the funds can be unstaked. /// Otherwise hopping in and out for inflation rewards is possible. WaitFullInflationPeriod, @@ -443,6 +438,20 @@ mod pallet { NoDelegations, /// The set of delegations should contain only valid and active juror accounts. DelegatedToInvalidJuror, + /// The market id to court id mapping was not found. + MarketIdToCourtIdNotFound, + /// The court id to market id mapping was not found. + CourtIdToMarketIdNotFound, + /// The vote item is not valid for this (outcome) court. + InvalidVoteItemForOutcomeCourt, + /// The vote item is not valid for this (binary) court. + InvalidVoteItemForBinaryCourt, + /// The appealed vote item is not an outcome. + AppealedVoteItemIsNotOutcome, + /// The winner vote item is not an outcome. + WinnerVoteItemIsNoOutcome, + /// The outcome does not match the market outcomes. + OutcomeMismatch, } #[pallet::hooks] @@ -652,7 +661,7 @@ mod pallet { /// /// # Arguments /// - /// - `market_id`: The identifier of the court. + /// - `court_id`: The identifier of the court. /// - `commitment_vote`: A hash which consists of `juror ++ vote_item ++ salt`. /// /// # Weight @@ -663,19 +672,19 @@ mod pallet { #[transactional] pub fn vote( origin: OriginFor, - #[pallet::compact] market_id: MarketIdOf, + #[pallet::compact] court_id: CourtId, commitment_vote: T::Hash, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( court.cycle_ends.pre_vote < now && now <= court.cycle_ends.vote, Error::::NotInVotingPeriod ); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); match draws.binary_search_by_key(&who, |draw| draw.juror.clone()) { Ok(index) => { @@ -695,11 +704,11 @@ mod pallet { let draws_len = draws.len() as u32; - >::insert(market_id, draws); + >::insert(court_id, draws); Self::deposit_event(Event::JurorVoted { juror: who, - market_id, + court_id, commitment: commitment_vote, }); @@ -713,7 +722,7 @@ mod pallet { /// /// # Arguments /// - /// - `market_id`: The identifier of the court. + /// - `court_id`: The identifier of the court. /// - `juror`: The juror whose commitment vote might be known. /// - `vote_item`: The raw vote item which should match with the commitment of the juror. /// - `salt`: The hash which is used to proof that the juror did reveal @@ -727,18 +736,16 @@ mod pallet { #[transactional] pub fn denounce_vote( origin: OriginFor, - #[pallet::compact] market_id: MarketIdOf, + #[pallet::compact] court_id: CourtId, juror: AccountIdLookupOf, - vote_item: T::VoteItem, + vote_item: VoteItem, salt: T::Hash, ) -> DispatchResultWithPostInfo { let denouncer = ensure_signed(origin)?; - let market = T::MarketCommons::market(&market_id)?; - ensure!( - market.matches_outcome_report(&vote_item.clone().into()), - Error::::OutcomeMismatch - ); + if let Some(market_id) = >::get(court_id) { + T::VoteCheck::pre_validate(&market_id, vote_item.clone())?; + } let juror = T::Lookup::lookup(juror)?; @@ -746,7 +753,9 @@ mod pallet { ensure!(>::contains_key(&juror), Error::::JurorDoesNotExist); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; + Self::check_vote_item(&court, &vote_item)?; + let now = >::block_number(); // ensure in vote period ensure!( @@ -754,7 +763,7 @@ mod pallet { Error::::NotInVotingPeriod ); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); match draws.binary_search_by_key(&juror, |draw| draw.juror.clone()) { Ok(index) => { let draw = draws[index].clone(); @@ -774,12 +783,12 @@ mod pallet { let draws_len = draws.len() as u32; - >::insert(market_id, draws); + >::insert(court_id, draws); Self::deposit_event(Event::DenouncedJurorVote { denouncer, juror, - market_id, + court_id, vote_item, salt, }); @@ -792,7 +801,7 @@ mod pallet { /// /// # Arguments /// - /// - `market_id`: The identifier of the court. + /// - `court_id`: The identifier of the court. /// - `vote_item`: The raw vote item which should match with the commitment of the juror. /// - `salt`: The hash which is used for the validation. /// @@ -804,27 +813,27 @@ mod pallet { #[transactional] pub fn reveal_vote( origin: OriginFor, - #[pallet::compact] market_id: MarketIdOf, - vote_item: T::VoteItem, + #[pallet::compact] court_id: CourtId, + vote_item: VoteItem, salt: T::Hash, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let market = T::MarketCommons::market(&market_id)?; - ensure!( - market.matches_outcome_report(&vote_item.clone().into()), - Error::::OutcomeMismatch - ); + if let Some(market_id) = >::get(court_id) { + T::VoteCheck::pre_validate(&market_id, vote_item.clone())?; + } ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; + Self::check_vote_item(&court, &vote_item)?; + let now = >::block_number(); ensure!( court.cycle_ends.vote < now && now <= court.cycle_ends.aggregation, Error::::NotInAggregationPeriod ); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); match draws.binary_search_by_key(&who, |draw| draw.juror.clone()) { Ok(index) => { let draw = draws[index].clone(); @@ -843,14 +852,9 @@ mod pallet { let draws_len = draws.len() as u32; - >::insert(market_id, draws); + >::insert(court_id, draws); - Self::deposit_event(Event::JurorRevealedVote { - juror: who, - market_id, - vote_item, - salt, - }); + Self::deposit_event(Event::JurorRevealedVote { juror: who, court_id, vote_item, salt }); Ok(Some(T::WeightInfo::reveal_vote(draws_len)).into()) } @@ -860,7 +864,7 @@ mod pallet { /// /// # Arguments /// - /// - `market_id`: The identifier of the court. + /// - `court_id`: The identifier of the court. /// /// # Weight /// @@ -875,22 +879,22 @@ mod pallet { #[transactional] pub fn appeal( origin: OriginFor, - market_id: MarketIdOf, + #[pallet::compact] court_id: CourtId, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let mut court = >::get(court_id).ok_or(Error::::CourtNotFound)?; let appeal_number = court.appeals.len().saturating_add(1); ensure!(appeal_number <= T::MaxAppeals::get() as usize, Error::::MaxAppealsReached); let bond = get_appeal_bond::(appeal_number); ensure!(T::Currency::can_reserve(&who, bond), Error::::AppealBondExceedsBalance); let now = >::block_number(); - Self::check_appealable_market(&market_id, &court, now)?; + Self::check_appealable_market(court_id, &court, now)?; // the vote item which would be resolved on is appealed (including oracle report) - let old_draws = SelectedDraws::::get(market_id); + let old_draws = SelectedDraws::::get(court_id); let appealed_vote_item = - Self::get_latest_winner_vote_item(&market_id, old_draws.as_slice())?; + Self::get_latest_winner_vote_item(court_id, old_draws.as_slice())?; let appeal_info = AppealInfo { backer: who.clone(), bond, appealed_vote_item }; court.appeals.try_push(appeal_info).map_err(|_| { debug_assert!(false, "Appeal bound is checked above."); @@ -903,7 +907,6 @@ mod pallet { let jurors_len = >::decode_len().unwrap_or(0) as u32; let mut ids_len_1 = 0u32; - // if appeal_number == MaxAppeals, then don't start a new appeal round if appeal_number < T::MaxAppeals::get() as usize { let new_draws = Self::select_jurors(appeal_number)?; @@ -919,19 +922,24 @@ mod pallet { court.update_lifecycle(round_timing); let new_resolve_at = court.cycle_ends.appeal; debug_assert!(new_resolve_at != last_resolve_at); - ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; - >::insert(market_id, new_draws); - Self::unlock_jurors_from_last_draw(&market_id, old_draws); + if let Some(market_id) = >::get(court_id) { + ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; + } + >::insert(court_id, new_draws); + Self::unlock_jurors_from_last_draw(court_id, old_draws); } - let ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + let mut ids_len_0 = 0u32; + if let Some(market_id) = >::get(court_id) { + ids_len_0 = T::DisputeResolution::remove_auto_resolve(&market_id, last_resolve_at); + } T::Currency::reserve_named(&Self::reserve_id(), &who, bond)?; - >::insert(market_id, court); + >::insert(court_id, court); let appeal_number = appeal_number as u32; - Self::deposit_event(Event::MarketAppealed { market_id, appeal_number }); + Self::deposit_event(Event::CourtAppealed { court_id, appeal_number }); Ok(Some(T::WeightInfo::appeal(jurors_len, appeal_number, ids_len_0, ids_len_1)).into()) } @@ -942,7 +950,7 @@ mod pallet { /// /// # Arguments /// - /// - `market_id`: The identifier of the court. + /// - `court_id`: The identifier of the court. /// /// # Weight /// @@ -951,21 +959,21 @@ mod pallet { #[transactional] pub fn reassign_juror_stakes( origin: OriginFor, - market_id: MarketIdOf, + court_id: CourtId, ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let mut court = >::get(court_id).ok_or(Error::::CourtNotFound)?; let winner = match court.status { CourtStatus::Closed { winner } => winner, CourtStatus::Reassigned => return Err(Error::::CourtAlreadyReassigned.into()), _ => return Err(Error::::CourtNotClosed.into()), }; - let draws = SelectedDraws::::get(market_id); + let draws = SelectedDraws::::get(court_id); let draws_len = draws.len() as u32; - let reward_pot = Self::reward_pot(&market_id); + let reward_pot = Self::reward_pot(court_id); let slash_juror = |ai: &T::AccountId, slashable: BalanceOf| { let (imbalance, missing) = T::Currency::slash(ai, slashable); debug_assert!( @@ -985,10 +993,10 @@ mod pallet { >::insert(&draw.juror, juror_info); } else { log::warn!( - "Juror {:?} not found in Jurors storage (reassign_juror_stakes). Market \ - id {:?}.", + "Juror {:?} not found in Jurors storage (reassign_juror_stakes). Court id \ + {:?}.", draw.juror, - market_id + court_id ); debug_assert!(false); } @@ -1036,14 +1044,14 @@ mod pallet { } } - Self::slash_losers_to_award_winners(&market_id, jurors_to_stakes, &winner); + Self::slash_losers_to_award_winners(court_id, jurors_to_stakes, &winner); court.status = CourtStatus::Reassigned; - >::insert(market_id, court); + >::insert(court_id, court); - >::remove(market_id); + >::remove(court_id); - Self::deposit_event(Event::JurorStakesReassigned { market_id }); + Self::deposit_event(Event::JurorStakesReassigned { court_id }); Ok(Some(T::WeightInfo::reassign_juror_stakes(draws_len)).into()) } @@ -1460,7 +1468,7 @@ mod pallet { // Reduce the active lock of the jurors from the last draw. // This is useful so that the jurors can thaw their non-locked stake. - fn unlock_jurors_from_last_draw(market_id: &MarketIdOf, last_draws: SelectedDrawsOf) { + fn unlock_jurors_from_last_draw(court_id: CourtId, last_draws: SelectedDrawsOf) { // keep in mind that the old draw likely contains different jurors for old_draw in last_draws { if let Some(mut juror_info) = >::get(&old_draw.juror) { @@ -1470,9 +1478,9 @@ mod pallet { } else { log::warn!( "Juror {:?} not found in Jurors storage (unlock_jurors_from_last_draw). \ - Market id {:?}.", + Court id {:?}.", old_draw.juror, - market_id + court_id ); debug_assert!(false); } @@ -1544,16 +1552,13 @@ mod pallet { // Returns OK if the market is in a valid state to be appealed. // Returns an error otherwise. pub(crate) fn check_appealable_market( - market_id: &MarketIdOf, + court_id: CourtId, court: &CourtOf, now: T::BlockNumber, ) -> Result<(), DispatchError> { - let market = T::MarketCommons::market(market_id)?; - ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Court, - Error::::MarketDoesNotHaveCourtMechanism - ); + if let Some(market_id) = >::get(court_id) { + T::AppealCheck::pre_appeal(&market_id)?; + } ensure!( court.cycle_ends.aggregation < now && now < court.cycle_ends.appeal, @@ -1571,8 +1576,8 @@ mod pallet { /// The account ID which is used to reward the correct jurors. #[inline] - pub fn reward_pot(market_id: &MarketIdOf) -> T::AccountId { - T::CourtPalletId::get().into_sub_account_truncating(market_id) + pub fn reward_pot(court_id: CourtId) -> T::AccountId { + T::CourtPalletId::get().into_sub_account_truncating(court_id) } /// The account ID of the treasury. @@ -1581,6 +1586,22 @@ mod pallet { T::TreasuryPalletId::get().into_account_truncating() } + pub(crate) fn check_vote_item( + court: &CourtOf, + vote_item: &VoteItem, + ) -> Result<(), DispatchError> { + match court.court_vote_item { + CourtVoteItem::Outcome => { + ensure!(vote_item.is_outcome(), Error::::InvalidVoteItemForOutcomeCourt); + } + CourtVoteItem::Binary => { + ensure!(vote_item.is_binary(), Error::::InvalidVoteItemForBinaryCourt); + } + }; + + Ok(()) + } + // Get a random seed based on a nonce. pub(crate) fn get_random_seed(nonce: u64) -> [u8; 32] { debug_assert!( @@ -1619,9 +1640,9 @@ mod pallet { // Slash the losers and use the slashed amount plus the reward pot to reward the winners. fn slash_losers_to_award_winners( - market_id: &MarketIdOf, + court_id: CourtId, jurors_to_stakes: BTreeMap>, - winner_vote_item: &T::VoteItem, + winner_vote_item: &VoteItem, ) { let mut total_incentives = >::zero(); @@ -1680,7 +1701,7 @@ mod pallet { } // reward from denounce slashes and tardy jurors of this market / court - let reward_pot = Self::reward_pot(market_id); + let reward_pot = Self::reward_pot(court_id); let reward = T::Currency::free_balance(&reward_pot); let (imb, missing) = T::Currency::slash(&reward_pot, reward); debug_assert!(missing.is_zero(), "Could not slash all of the amount for reward pot."); @@ -1709,9 +1730,9 @@ mod pallet { // If the best two vote items have the same score, returns the last court round winner. pub(crate) fn get_winner( draws: &[DrawOf], - last_winner: Option, - ) -> Option { - let mut scores = BTreeMap::::new(); + last_winner: Option, + ) -> Option { + let mut scores = BTreeMap::::new(); for draw in draws { if let Vote::Revealed { commitment: _, vote_item, salt: _ } = &draw.vote { @@ -1753,34 +1774,31 @@ mod pallet { Some(best_score.0.clone()) } - // TODO make this independent / generic from the market and oracle outcome. // Returns the vote item, on which the market would resolve // if the current court round is the final (not appealed) court round. pub(crate) fn get_latest_winner_vote_item( - market_id: &MarketIdOf, + court_id: CourtId, last_draws: &[DrawOf], - ) -> Result { - let market = T::MarketCommons::market(market_id)?; - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let last_winner: Option = court + ) -> Result { + let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; + let last_winner: Option = court .appeals .last() .map(|appeal_info| Some(appeal_info.appealed_vote_item.clone())) .unwrap_or(None); - // TODO use some generic form to input the default vote item (Query with a trait) - let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; - let oracle_outcome = report.outcome.clone(); - let winner_vote_item = - Self::get_winner(last_draws, last_winner).unwrap_or_else(|| oracle_outcome.into()); + let market_id = >::get(court_id) + .ok_or(Error::::CourtIdToMarketIdNotFound)?; + let default: VoteItem = T::DefaultWinner::default_winner(&market_id)?; + let winner_vote_item = Self::get_winner(last_draws, last_winner).unwrap_or(default); Ok(winner_vote_item) } // Check if the (juror, vote_item, salt) combination matches the secret hash of the vote. pub(crate) fn is_valid(commitment_matcher: CommitmentMatcherOf) -> DispatchResult { // market id and current appeal number is part of salt generation - // salt should be signed by the juror (market_id ++ appeal number) + // salt should be signed by the juror (court_id ++ appeal number) // salt can be reproduced only be the juror address - // with knowing market_id and appeal number + // with knowing court_id and appeal number // so even if the salt is forgotten it can be reproduced only by the juror let CommitmentMatcher { hashed: commitment, @@ -1875,7 +1893,9 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - ensure!(!>::contains_key(market_id), Error::::CourtAlreadyExists); + let court_id = >::get(); + let next_court_id = + court_id.checked_add(One::one()).ok_or(Error::::MaxCourtIdReached)?; let appeal_number = 0usize; let jurors_len = >::decode_len().unwrap_or(0) as u32; @@ -1891,14 +1911,18 @@ mod pallet { appeal_period: T::CourtAppealPeriod::get(), }; + let court_vote_item = CourtVoteItem::Outcome; // sets cycle_ends one after the other from now - let court = CourtInfo::new(round_timing); + let court = CourtInfo::new(round_timing, court_vote_item); let ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.cycle_ends.appeal)?; - >::insert(market_id, new_draws); - >::insert(market_id, court); + >::insert(court_id, new_draws); + >::insert(court_id, court); + >::insert(market_id, court_id); + >::insert(court_id, market_id); + >::put(next_court_id); let res = ResultWithWeightInfo { result: (), @@ -1917,16 +1941,21 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let mut court = >::get(market_id).ok_or(Error::::CourtNotFound)?; - let draws = SelectedDraws::::get(market_id); + let court_id = >::get(market_id) + .ok_or(Error::::MarketIdToCourtIdNotFound)?; + let mut court = >::get(court_id).ok_or(Error::::CourtNotFound)?; + let draws = SelectedDraws::::get(court_id); let draws_len = draws.len() as u32; - let winner_vote_item = Self::get_latest_winner_vote_item(market_id, draws.as_slice())?; - Self::unlock_jurors_from_last_draw(market_id, draws); + let winner_vote_item = Self::get_latest_winner_vote_item(court_id, draws.as_slice())?; + Self::unlock_jurors_from_last_draw(court_id, draws); court.status = CourtStatus::Closed { winner: winner_vote_item.clone() }; - >::insert(market_id, court); + >::insert(court_id, court); + + let winner_outcome = + winner_vote_item.into_outcome().ok_or(Error::::WinnerVoteItemIsNoOutcome)?; let res = ResultWithWeightInfo { - result: Some(winner_vote_item.into()), + result: Some(winner_outcome), weight: T::WeightInfo::on_resolution(T::MaxJurors::get(), draws_len), }; @@ -1944,10 +1973,15 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let court_id = >::get(market_id) + .ok_or(Error::::MarketIdToCourtIdNotFound)?; + let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; let appeals_len = court.appeals.len() as u32; for AppealInfo { backer, bond, appealed_vote_item } in &court.appeals { - let appealed_vote_item_as_outcome = appealed_vote_item.clone().into(); + let appealed_vote_item_as_outcome = appealed_vote_item + .clone() + .into_outcome() + .ok_or(Error::::AppealedVoteItemIsNotOutcome)?; if resolved_outcome == &appealed_vote_item_as_outcome { let (imb, missing) = T::Currency::slash_reserved_named(&Self::reserve_id(), backer, *bond); @@ -1963,6 +1997,7 @@ mod pallet { result: overall_imbalance, weight: T::WeightInfo::exchange(appeals_len), }; + Ok(res) } @@ -1975,8 +2010,11 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); + let court_id = >::get(market_id) + .ok_or(Error::::MarketIdToCourtIdNotFound)?; + let res = ResultWithWeightInfo { - result: >::get(market_id).map(|court| court.cycle_ends.appeal), + result: >::get(court_id).map(|court| court.cycle_ends.appeal), weight: T::WeightInfo::get_auto_resolve(), }; @@ -1995,14 +2033,16 @@ mod pallet { let mut has_failed = false; let now = >::block_number(); + let court_id = >::get(market_id) + .ok_or(Error::::MarketIdToCourtIdNotFound)?; + let jurors_len: usize = JurorPool::::decode_len().unwrap_or(0); - match >::get(market_id) { + match >::get(court_id) { Some(court) => { let appeals = &court.appeals; let appeal_number = appeals.len().saturating_add(1); let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); - let valid_period = - Self::check_appealable_market(market_id, &court, now).is_ok(); + let valid_period = Self::check_appealable_market(court_id, &court, now).is_ok(); if appeals.is_full() || (valid_period && (jurors_len < necessary_jurors_weight)) { @@ -2042,7 +2082,10 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let court = >::get(market_id).ok_or(Error::::CourtNotFound)?; + let court_id = >::get(market_id) + .ok_or(Error::::MarketIdToCourtIdNotFound)?; + + let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; let oracle_outcome = &report.outcome; @@ -2052,24 +2095,26 @@ mod pallet { let gd_outcomes = court .appeals .iter() - // oracle outcome is added by pm pallet - .filter(|a| &a.appealed_vote_item.clone().into() != oracle_outcome) - .map(|a| { - GlobalDisputeItem { - outcome: a.appealed_vote_item.clone().into(), - // we have no better global dispute outcome owner - owner: Self::treasury_account_id(), - // initial vote amount - initial_vote_amount: >::zero(), + .filter_map(|a| { + match a.appealed_vote_item.clone().into_outcome() { + // oracle outcome is added by pm pallet + Some(outcome) if outcome != *oracle_outcome => Some(GlobalDisputeItem { + outcome, + // we have no better global dispute outcome owner + owner: Self::treasury_account_id(), + // initial vote amount + initial_vote_amount: >::zero(), + }), + _ => None, } }) .collect::>>(); - let old_draws = SelectedDraws::::get(market_id); + let old_draws = SelectedDraws::::get(court_id); let draws_len = old_draws.len() as u32; - Self::unlock_jurors_from_last_draw(market_id, old_draws); - >::remove(market_id); - >::remove(market_id); + Self::unlock_jurors_from_last_draw(court_id, old_draws); + >::remove(court_id); + >::remove(court_id); let res = ResultWithWeightInfo { result: gd_outcomes, @@ -2088,11 +2133,14 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let old_draws = SelectedDraws::::get(market_id); + let court_id = >::get(market_id) + .ok_or(Error::::MarketIdToCourtIdNotFound)?; + + let old_draws = SelectedDraws::::get(court_id); let draws_len = old_draws.len() as u32; - Self::unlock_jurors_from_last_draw(market_id, old_draws); - >::remove(market_id); - >::remove(market_id); + Self::unlock_jurors_from_last_draw(court_id, old_draws); + >::remove(court_id); + >::remove(court_id); let res = ResultWithWeightInfo { result: (), weight: T::WeightInfo::clear(draws_len) }; diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 7080e56f1..47b8dd688 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -18,9 +18,14 @@ #![cfg(test)] -use crate::{self as zrml_court, mock_storage::pallet as mock_storage}; +use crate::{ + self as zrml_court, + mock_storage::pallet as mock_storage, + traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, + Error as PError, VoteItem, +}; use frame_support::{ - construct_runtime, ord_parameter_types, + construct_runtime, ensure, ord_parameter_types, pallet_prelude::{DispatchError, Weight}, parameter_types, traits::{Everything, Hooks, NeverEnsureOrigin}, @@ -40,10 +45,11 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, - Moment, UncheckedExtrinsicTest, VoteItem, + AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, + MarketDisputeMechanism, MarketId, MarketStatus, Moment, UncheckedExtrinsicTest, }, }; +use zrml_market_commons::MarketCommonsPalletApi; pub const ALICE: AccountIdTest = 0; pub const BOB: AccountIdTest = 1; @@ -137,14 +143,57 @@ impl DisputeResolutionApi for MockResolution { } } +pub struct AppealCheck; +impl AppealCheckApi for AppealCheck { + type MarketId = MarketId; + + fn pre_appeal(market_id: &Self::MarketId) -> Result<(), DispatchError> { + let market = MarketCommons::market(market_id)?; + ensure!(market.status == MarketStatus::Disputed, PError::::MarketIsNotDisputed); + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + PError::::MarketDoesNotHaveCourtMechanism + ); + Ok(()) + } +} + +pub struct VoteCheck; +impl VoteCheckApi for VoteCheck { + type MarketId = MarketId; + + fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), DispatchError> { + let market = MarketCommons::market(market_id)?; + ensure!( + market.matches_outcome_report(&vote_item.into_outcome().unwrap()), + PError::::OutcomeMismatch + ); + Ok(()) + } +} + +pub struct DefaultWinner; +impl DefaultWinnerApi for DefaultWinner { + type MarketId = MarketId; + + fn default_winner(market_id: &Self::MarketId) -> Result { + let market = MarketCommons::market(market_id)?; + let report = market.report.as_ref().ok_or(PError::::MarketReportNotFound)?; + let vote_item = VoteItem::Outcome(report.outcome.clone()); + Ok(vote_item) + } +} + impl crate::Config for Runtime { type AppealBond = AppealBond; + type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; type CourtLockId = CourtLockId; type Currency = Balances; type CourtVotePeriod = CourtVotePeriod; type CourtAggregationPeriod = CourtAggregationPeriod; type CourtAppealPeriod = CourtAppealPeriod; + type DefaultWinner = DefaultWinner; type DisputeResolution = MockResolution; type Event = Event; type InflationPeriod = InflationPeriod; @@ -160,7 +209,7 @@ impl crate::Config for Runtime { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; - type VoteItem = VoteItem; + type VoteCheck = VoteCheck; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 31d632644..c0b594fd0 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -26,9 +26,10 @@ use crate::{ POOR_PAUL, }, mock_storage::pallet::MarketIdsPerDisputeBlock, - types::{CourtStatus, Draw, Vote}, - AppealInfo, BalanceOf, Courts, Error, Event, JurorInfo, JurorInfoOf, JurorPool, JurorPoolItem, - JurorPoolOf, Jurors, MarketIdOf, MarketOf, NegativeImbalanceOf, RequestBlock, SelectedDraws, + types::{CourtStatus, Draw, Vote, VoteItem}, + AppealInfo, BalanceOf, CourtId, CourtIdToMarketId, Courts, Error, Event, JurorInfo, + JurorInfoOf, JurorPool, JurorPoolItem, JurorPoolOf, Jurors, MarketIdToCourtId, MarketOf, + NegativeImbalanceOf, RequestBlock, SelectedDraws, }; use alloc::collections::BTreeMap; use frame_support::{ @@ -79,7 +80,7 @@ const DEFAULT_MARKET: MarketOf = Market { bonds: MarketBonds { creation: None, oracle: None, outsider: None, dispute: None }, }; -fn initialize_court() -> MarketIdOf { +fn initialize_court() -> CourtId { let now = >::block_number(); >::put(now + RequestInterval::get()); let amount_alice = 2 * BASE; @@ -99,7 +100,7 @@ fn initialize_court() -> MarketIdOf { }) .unwrap(); Court::on_dispute(&market_id, &DEFAULT_MARKET).unwrap(); - market_id + >::get(market_id).unwrap() } fn fill_juror_pool() { @@ -111,13 +112,12 @@ fn fill_juror_pool() { } } -fn fill_appeals(market_id: &MarketIdOf, appeal_number: usize) { +fn fill_appeals(court_id: CourtId, appeal_number: usize) { assert!(appeal_number <= MaxAppeals::get() as usize); - let mut court = Courts::::get(market_id).unwrap(); + let mut court = Courts::::get(court_id).unwrap(); let mut number = 0u128; while (number as usize) < appeal_number { - let appealed_vote_item: ::VoteItem = - OutcomeReport::Scalar(number); + let appealed_vote_item: VoteItem = VoteItem::Outcome(OutcomeReport::Scalar(number)); court .appeals .try_push(AppealInfo { @@ -128,16 +128,16 @@ fn fill_appeals(market_id: &MarketIdOf, appeal_number: usize) { .unwrap(); number += 1; } - Courts::::insert(market_id, court); + Courts::::insert(court_id, court); } -fn put_alice_in_draw(market_id: MarketIdOf, stake: BalanceOf) { +fn put_alice_in_draw(court_id: CourtId, stake: BalanceOf) { // trick a little bit to let alice be part of the ("random") selection - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); assert!(!draws.is_empty()); let slashable = MinJurorStake::get(); draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; - >::insert(market_id, draws); + >::insert(court_id, draws); >::insert( ALICE, JurorInfo { @@ -151,26 +151,23 @@ fn put_alice_in_draw(market_id: MarketIdOf, stake: BalanceOf) fn set_alice_after_vote( outcome: OutcomeReport, -) -> ( - MarketIdOf, - ::Hash, - ::Hash, -) { +) -> (CourtId, ::Hash, ::Hash) { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id, amount); + put_alice_in_draw(court_id, amount); run_to_block(>::get() + 1); let salt = ::Hash::default(); - let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); + let vote_item = VoteItem::Outcome(outcome); + let commitment = BlakeTwo256::hash_of(&(ALICE, vote_item, salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), court_id, commitment)); - (market_id, commitment, salt) + (court_id, commitment, salt) } const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ @@ -644,19 +641,19 @@ fn exit_court_fails_if_inflation_period_not_over() { fn vote_works() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); // trick a little bit to let alice be part of the ("random") selection - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); assert_eq!(draws.len(), 5usize); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; - >::insert(market_id, draws); + >::insert(court_id, draws); >::insert( ALICE, JurorInfo { @@ -667,17 +664,17 @@ fn vote_works() { }, ); - let old_draws = >::get(market_id); + let old_draws = >::get(court_id); run_to_block(>::get() + 1); let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); - System::assert_last_event(Event::JurorVoted { juror: ALICE, market_id, commitment }.into()); + assert_ok!(Court::vote(Origin::signed(ALICE), court_id, commitment)); + System::assert_last_event(Event::JurorVoted { court_id, juror: ALICE, commitment }.into()); - let new_draws = >::get(market_id); + let new_draws = >::get(court_id); for (i, (old_draw, new_draw)) in old_draws.iter().zip(new_draws.iter()).enumerate() { if i == alice_index { continue; @@ -697,21 +694,21 @@ fn vote_works() { fn vote_overwrite_works() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id, amount); + put_alice_in_draw(court_id, amount); run_to_block(>::get() + 1); let wrong_outcome = OutcomeReport::Scalar(69u128); let salt = ::Hash::default(); let wrong_commitment = BlakeTwo256::hash_of(&(ALICE, wrong_outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, wrong_commitment)); + assert_ok!(Court::vote(Origin::signed(ALICE), court_id, wrong_commitment)); assert_eq!( - >::get(market_id)[0].vote, + >::get(court_id)[0].vote, Vote::Secret { commitment: wrong_commitment } ); @@ -719,10 +716,10 @@ fn vote_overwrite_works() { let right_outcome = OutcomeReport::Scalar(42u128); let new_commitment = BlakeTwo256::hash_of(&(ALICE, right_outcome, salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, new_commitment)); + assert_ok!(Court::vote(Origin::signed(ALICE), court_id, new_commitment)); assert_ne!(wrong_commitment, new_commitment); assert_eq!( - >::get(market_id)[0].vote, + >::get(court_id)[0].vote, Vote::Secret { commitment: new_commitment } ); }); @@ -731,10 +728,10 @@ fn vote_overwrite_works() { #[test] fn vote_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { - let market_id = 0; + let court_id = 0; let commitment = ::Hash::default(); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, commitment), + Court::vote(Origin::signed(ALICE), court_id, commitment), Error::::CourtNotFound ); }); @@ -743,35 +740,31 @@ fn vote_fails_if_court_not_found() { #[test_case( Vote::Revealed { commitment: ::Hash::default(), - vote_item: OutcomeReport::Scalar(1u128), + vote_item: VoteItem::Outcome(OutcomeReport::Scalar(1u128)), salt: ::Hash::default(), }; "revealed" )] #[test_case( Vote::Denounced { commitment: ::Hash::default(), - vote_item: OutcomeReport::Scalar(1u128), + vote_item: VoteItem::Outcome(OutcomeReport::Scalar(1u128)), salt: ::Hash::default(), }; "denounced" )] fn vote_fails_if_vote_state_incorrect( - vote: crate::Vote< - ::Hash, - crate::DelegatedStakesOf, - ::VoteItem, - >, + vote: crate::Vote<::Hash, crate::DelegatedStakesOf>, ) { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); assert!(!draws.is_empty()); draws[0] = Draw { juror: ALICE, weight: 101, vote, slashable: 42u128 }; - >::insert(market_id, draws); + >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -779,7 +772,7 @@ fn vote_fails_if_vote_state_incorrect( let salt = ::Hash::default(); let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, commitment), + Court::vote(Origin::signed(ALICE), court_id, commitment), Error::::InvalidVoteState ); }); @@ -789,11 +782,11 @@ fn vote_fails_if_vote_state_incorrect( fn vote_fails_if_caller_not_in_draws() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); draws.retain(|draw| draw.juror != ALICE); - >::insert(market_id, draws); + >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -801,7 +794,7 @@ fn vote_fails_if_caller_not_in_draws() { let salt = ::Hash::default(); let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, commitment), + Court::vote(Origin::signed(ALICE), court_id, commitment), Error::::CallerNotInSelectedDraws ); }); @@ -811,12 +804,12 @@ fn vote_fails_if_caller_not_in_draws() { fn vote_fails_if_not_in_voting_period() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - put_alice_in_draw(market_id, amount); + put_alice_in_draw(court_id, amount); run_to_block(>::get() + CourtVotePeriod::get() + 1); @@ -824,7 +817,7 @@ fn vote_fails_if_not_in_voting_period() { let salt = ::Hash::default(); let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); assert_noop!( - Court::vote(Origin::signed(ALICE), market_id, commitment), + Court::vote(Origin::signed(ALICE), court_id, commitment), Error::::NotInVotingPeriod ); }); @@ -834,19 +827,19 @@ fn vote_fails_if_not_in_voting_period() { fn reveal_vote_works() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); // trick a little bit to let alice be part of the ("random") selection - let mut draws = >::get(market_id); + let mut draws = >::get(court_id); assert_eq!(draws.len(), 5usize); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; - >::insert(market_id, draws); + >::insert(court_id, draws); >::insert( ALICE, JurorInfo { @@ -860,21 +853,23 @@ fn reveal_vote_works() { run_to_block(>::get() + 1); let outcome = OutcomeReport::Scalar(42u128); + let vote_item = VoteItem::Outcome(outcome); + let salt = ::Hash::default(); - let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); + let commitment = BlakeTwo256::hash_of(&(ALICE, vote_item.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(ALICE), court_id, commitment)); - let old_draws = >::get(market_id); + let old_draws = >::get(court_id); run_blocks(CourtVotePeriod::get() + 1); - assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt,)); + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item.clone(), salt,)); System::assert_last_event( - Event::JurorRevealedVote { juror: ALICE, market_id, vote_item: outcome.clone(), salt } + Event::JurorRevealedVote { juror: ALICE, court_id, vote_item: vote_item.clone(), salt } .into(), ); - let new_draws = >::get(market_id); + let new_draws = >::get(court_id); for (i, (old_draw, new_draw)) in old_draws.iter().zip(new_draws.iter()).enumerate() { if i == alice_index { continue; @@ -885,10 +880,7 @@ fn reveal_vote_works() { assert_eq!(old_draws[alice_index].weight, new_draws[alice_index].weight); assert_eq!(old_draws[alice_index].slashable, new_draws[alice_index].slashable); assert_eq!(old_draws[alice_index].vote, Vote::Secret { commitment }); - assert_eq!( - new_draws[alice_index].vote, - Vote::Revealed { commitment, vote_item: outcome, salt } - ); + assert_eq!(new_draws[alice_index].vote, Vote::Revealed { commitment, vote_item, salt }); }); } @@ -896,14 +888,16 @@ fn reveal_vote_works() { fn reveal_vote_fails_if_caller_not_juror() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); >::remove(ALICE); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::OnlyJurorsCanReveal ); }); @@ -913,13 +907,15 @@ fn reveal_vote_fails_if_caller_not_juror() { fn reveal_vote_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); - >::remove(market_id); + >::remove(court_id); + + let vote_item = VoteItem::Outcome(outcome); assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::CourtNotFound ); }); @@ -929,12 +925,14 @@ fn reveal_vote_fails_if_court_not_found() { fn reveal_vote_fails_if_not_in_aggregation_period() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::NotInAggregationPeriod ); }); @@ -944,16 +942,18 @@ fn reveal_vote_fails_if_not_in_aggregation_period() { fn reveal_vote_fails_if_juror_not_drawn() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); - >::mutate(market_id, |draws| { + >::mutate(court_id, |draws| { draws.retain(|draw| draw.juror != ALICE); }); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::JurorNotDrawn ); }); @@ -963,13 +963,14 @@ fn reveal_vote_fails_if_juror_not_drawn() { fn reveal_vote_fails_for_invalid_reveal() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome); + let (court_id, _, salt) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + 1); let invalid_outcome = OutcomeReport::Scalar(43u128); + let invalid_vote_item = VoteItem::Outcome(invalid_outcome); assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, invalid_outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, invalid_vote_item, salt), Error::::InvalidReveal ); }); @@ -979,14 +980,16 @@ fn reveal_vote_fails_for_invalid_reveal() { fn reveal_vote_fails_for_invalid_salt() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, correct_salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, correct_salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); let incorrect_salt: ::Hash = [42; 32].into(); assert_ne!(correct_salt, incorrect_salt); + + let vote_item = VoteItem::Outcome(outcome); assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, incorrect_salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, incorrect_salt), Error::::InvalidReveal ); }); @@ -996,11 +999,11 @@ fn reveal_vote_fails_for_invalid_salt() { fn reveal_vote_fails_if_juror_not_voted() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); - >::mutate(market_id, |draws| { + >::mutate(court_id, |draws| { draws.iter_mut().for_each(|draw| { if draw.juror == ALICE { draw.vote = Vote::Drawn; @@ -1008,8 +1011,10 @@ fn reveal_vote_fails_if_juror_not_voted() { }); }); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::JurorNotVoted ); }); @@ -1019,14 +1024,16 @@ fn reveal_vote_fails_if_juror_not_voted() { fn reveal_vote_fails_if_already_revealed() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); - assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt)); + let vote_item = VoteItem::Outcome(outcome); + + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item.clone(), salt)); assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::VoteAlreadyRevealed ); }); @@ -1036,20 +1043,22 @@ fn reveal_vote_fails_if_already_revealed() { fn reveal_vote_fails_if_already_denounced() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); + + let vote_item = VoteItem::Outcome(outcome); assert_ok!(Court::denounce_vote( Origin::signed(BOB), - market_id, + court_id, ALICE, - outcome.clone(), + vote_item.clone(), salt )); run_blocks(CourtVotePeriod::get() + 1); assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::VoteAlreadyDenounced ); }); @@ -1059,9 +1068,9 @@ fn reveal_vote_fails_if_already_denounced() { fn denounce_vote_works() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, commitment, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, commitment, salt) = set_alice_after_vote(outcome.clone()); - let old_draws = >::get(market_id); + let old_draws = >::get(court_id); assert!( old_draws .iter() @@ -1069,34 +1078,36 @@ fn denounce_vote_works() { ); let free_alice_before = Balances::free_balance(ALICE); - let pot_balance_before = Balances::free_balance(&Court::reward_pot(&market_id)); + let pot_balance_before = Balances::free_balance(&Court::reward_pot(court_id)); + + let vote_item = VoteItem::Outcome(outcome); assert_ok!(Court::denounce_vote( Origin::signed(BOB), - market_id, + court_id, ALICE, - outcome.clone(), + vote_item.clone(), salt )); System::assert_last_event( Event::DenouncedJurorVote { denouncer: BOB, juror: ALICE, - market_id, - vote_item: outcome.clone(), + court_id, + vote_item: vote_item.clone(), salt, } .into(), ); - let new_draws = >::get(market_id); + let new_draws = >::get(court_id); assert_eq!(old_draws[1..], new_draws[1..]); assert_eq!(old_draws[0].juror, ALICE); assert_eq!(old_draws[0].juror, new_draws[0].juror); assert_eq!(old_draws[0].weight, new_draws[0].weight); assert_eq!(old_draws[0].slashable, new_draws[0].slashable); assert_eq!(old_draws[0].vote, Vote::Secret { commitment }); - assert_eq!(new_draws[0].vote, Vote::Denounced { commitment, vote_item: outcome, salt }); + assert_eq!(new_draws[0].vote, Vote::Denounced { commitment, vote_item, salt }); let free_alice_after = Balances::free_balance(ALICE); let slash = old_draws[0].slashable; @@ -1105,7 +1116,7 @@ fn denounce_vote_works() { // see `reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners` assert_eq!(free_alice_after, free_alice_before); - let pot_balance_after = Balances::free_balance(&Court::reward_pot(&market_id)); + let pot_balance_after = Balances::free_balance(&Court::reward_pot(court_id)); assert_eq!(pot_balance_after, pot_balance_before); }); } @@ -1114,10 +1125,12 @@ fn denounce_vote_works() { fn denounce_vote_fails_if_self_denounce() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); + + let vote_item = VoteItem::Outcome(outcome); assert_noop!( - Court::denounce_vote(Origin::signed(ALICE), market_id, ALICE, outcome, salt), + Court::denounce_vote(Origin::signed(ALICE), court_id, ALICE, vote_item, salt), Error::::SelfDenounceDisallowed ); }); @@ -1127,12 +1140,14 @@ fn denounce_vote_fails_if_self_denounce() { fn denounce_vote_fails_if_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); >::remove(ALICE); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, vote_item, salt), Error::::JurorDoesNotExist ); }); @@ -1142,12 +1157,14 @@ fn denounce_vote_fails_if_juror_does_not_exist() { fn denounce_vote_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); + + >::remove(court_id); - >::remove(market_id); + let vote_item = VoteItem::Outcome(outcome); assert_noop!( - Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, vote_item, salt), Error::::CourtNotFound ); }); @@ -1157,12 +1174,14 @@ fn denounce_vote_fails_if_court_not_found() { fn denounce_vote_fails_if_not_in_voting_period() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, vote_item, salt), Error::::NotInVotingPeriod ); }); @@ -1172,14 +1191,16 @@ fn denounce_vote_fails_if_not_in_voting_period() { fn denounce_vote_fails_if_juror_not_drawn() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - >::mutate(market_id, |draws| { + >::mutate(court_id, |draws| { draws.retain(|draw| draw.juror != ALICE); }); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, vote_item, salt), Error::::JurorNotDrawn ); }); @@ -1189,11 +1210,12 @@ fn denounce_vote_fails_if_juror_not_drawn() { fn denounce_vote_fails_if_invalid_reveal() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome); + let (court_id, _, salt) = set_alice_after_vote(outcome); let invalid_outcome = OutcomeReport::Scalar(69u128); + let invalid_vote_item = VoteItem::Outcome(invalid_outcome); assert_noop!( - Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, invalid_outcome, salt), + Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, invalid_vote_item, salt), Error::::InvalidReveal ); }); @@ -1203,9 +1225,9 @@ fn denounce_vote_fails_if_invalid_reveal() { fn denounce_vote_fails_if_juror_not_voted() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - >::mutate(market_id, |draws| { + >::mutate(court_id, |draws| { draws.iter_mut().for_each(|draw| { if draw.juror == ALICE { draw.vote = Vote::Drawn; @@ -1213,8 +1235,10 @@ fn denounce_vote_fails_if_juror_not_voted() { }); }); + let vote_item = VoteItem::Outcome(outcome); + assert_noop!( - Court::denounce_vote(Origin::signed(BOB), market_id, ALICE, outcome, salt), + Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, vote_item, salt), Error::::JurorNotVoted ); }); @@ -1224,14 +1248,16 @@ fn denounce_vote_fails_if_juror_not_voted() { fn denounce_vote_fails_if_vote_already_revealed() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); run_blocks(CourtVotePeriod::get() + 1); - assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt)); + let vote_item = VoteItem::Outcome(outcome); + + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item.clone(), salt)); assert_noop!( - Court::reveal_vote(Origin::signed(ALICE), market_id, outcome, salt), + Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), Error::::VoteAlreadyRevealed ); }); @@ -1241,18 +1267,20 @@ fn denounce_vote_fails_if_vote_already_revealed() { fn denounce_vote_fails_if_vote_already_denounced() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, salt) = set_alice_after_vote(outcome.clone()); + let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); + + let vote_item = VoteItem::Outcome(outcome); assert_ok!(Court::denounce_vote( Origin::signed(BOB), - market_id, + court_id, ALICE, - outcome.clone(), + vote_item.clone(), salt )); assert_noop!( - Court::denounce_vote(Origin::signed(CHARLIE), market_id, ALICE, outcome, salt), + Court::denounce_vote(Origin::signed(CHARLIE), court_id, ALICE, vote_item, salt), Error::::VoteAlreadyDenounced ); }); @@ -1262,16 +1290,16 @@ fn denounce_vote_fails_if_vote_already_denounced() { fn appeal_updates_cycle_ends() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); - let last_court = >::get(market_id).unwrap(); + let last_court = >::get(court_id).unwrap(); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); let now = >::block_number(); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let request_block = >::get(); assert!(now < request_block); @@ -1300,12 +1328,12 @@ fn appeal_updates_cycle_ends() { fn appeal_reserves_get_appeal_bond() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); let free_charlie_before = Balances::free_balance(CHARLIE); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); let free_charlie_after = Balances::free_balance(CHARLIE); let bond = crate::get_appeal_bond::(1usize); @@ -1319,13 +1347,13 @@ fn appeal_reserves_get_appeal_bond() { fn appeal_emits_event() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - System::assert_last_event(Event::MarketAppealed { market_id, appeal_number: 1u32 }.into()); + System::assert_last_event(Event::CourtAppealed { court_id, appeal_number: 1u32 }.into()); }); } @@ -1333,16 +1361,16 @@ fn appeal_emits_event() { fn appeal_shifts_auto_resolve() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); - let resolve_at_0 = >::get(market_id).unwrap().cycle_ends.appeal; + let resolve_at_0 = >::get(court_id).unwrap().cycle_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![0]); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let resolve_at_1 = >::get(market_id).unwrap().cycle_ends.appeal; + let resolve_at_1 = >::get(court_id).unwrap().cycle_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_1), vec![0]); assert_ne!(resolve_at_0, resolve_at_1); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![]); @@ -1353,16 +1381,16 @@ fn appeal_shifts_auto_resolve() { fn appeal_overrides_last_draws() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); - let last_draws = >::get(market_id); + let last_draws = >::get(court_id); assert!(!last_draws.len().is_zero()); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let draws = >::get(market_id); + let draws = >::get(court_id); assert_ne!(draws, last_draws); }); } @@ -1371,18 +1399,18 @@ fn appeal_overrides_last_draws() { fn appeal_draws_total_weight_is_correct() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); - let last_draws = >::get(market_id); + let last_draws = >::get(court_id); let last_draws_total_weight = last_draws.iter().map(|draw| draw.weight).sum::(); assert_eq!(last_draws_total_weight, Court::necessary_jurors_weight(0usize) as u32); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); let neccessary_juror_weight = Court::necessary_jurors_weight(1usize) as u32; - let draws = >::get(market_id); + let draws = >::get(court_id); let draws_total_weight = draws.iter().map(|draw| draw.weight).sum::(); assert_eq!(draws_total_weight, neccessary_juror_weight); }); @@ -1392,13 +1420,13 @@ fn appeal_draws_total_weight_is_correct() { fn appeal_get_latest_resolved_outcome_changes() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let last_appealed_vote_item = >::get(market_id) + let last_appealed_vote_item = >::get(court_id) .unwrap() .appeals .last() @@ -1409,22 +1437,25 @@ fn appeal_get_latest_resolved_outcome_changes() { let request_block = >::get(); run_to_block(request_block + 1); let outcome = OutcomeReport::Scalar(69u128); + let vote_item = VoteItem::Outcome(outcome.clone()); let salt = ::Hash::default(); - let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, vote_item, salt)); // cheat a little to get alice in the draw for the new appeal - put_alice_in_draw(market_id, MinJurorStake::get()); - assert_ok!(Court::vote(Origin::signed(ALICE), market_id, commitment)); + put_alice_in_draw(court_id, MinJurorStake::get()); + assert_ok!(Court::vote(Origin::signed(ALICE), court_id, commitment)); run_blocks(CourtVotePeriod::get() + 1); - assert_ok!(Court::reveal_vote(Origin::signed(ALICE), market_id, outcome.clone(), salt)); + let vote_item = VoteItem::Outcome(outcome); + + assert_ok!(Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item.clone(), salt)); run_blocks(CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let new_appealed_vote_item = >::get(market_id) + let new_appealed_vote_item = >::get(court_id) .unwrap() .appeals .last() @@ -1434,7 +1465,7 @@ fn appeal_get_latest_resolved_outcome_changes() { // if the new appealed outcome were the last appealed outcome, // then the wrong appealed outcome was added in `appeal` - assert_eq!(new_appealed_vote_item, outcome); + assert_eq!(new_appealed_vote_item, vote_item); assert_ne!(last_appealed_vote_item, new_appealed_vote_item); }); } @@ -1450,12 +1481,12 @@ fn appeal_fails_if_court_not_found() { fn appeal_fails_if_appeal_bond_exceeds_balance() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); assert_noop!( - Court::appeal(Origin::signed(POOR_PAUL), market_id), + Court::appeal(Origin::signed(POOR_PAUL), court_id), Error::::AppealBondExceedsBalance ); }); @@ -1465,14 +1496,14 @@ fn appeal_fails_if_appeal_bond_exceeds_balance() { fn appeal_fails_if_max_appeals_reached() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); - fill_appeals(&market_id, MaxAppeals::get() as usize); + fill_appeals(court_id, MaxAppeals::get() as usize); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); assert_noop!( - Court::appeal(Origin::signed(CHARLIE), market_id), + Court::appeal(Origin::signed(CHARLIE), court_id), Error::::MaxAppealsReached ); }); @@ -1483,15 +1514,15 @@ fn check_appealable_market_fails_if_market_not_found() { ExtBuilder::default().build().execute_with(|| { let now = >::block_number(); let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - let court = >::get(market_id).unwrap(); - MarketCommons::remove_market(&market_id).unwrap(); + let court = >::get(court_id).unwrap(); + MarketCommons::remove_market(&court_id).unwrap(); assert_noop!( - Court::check_appealable_market(&0, &court, now), + Court::check_appealable_market(court_id, &court, now), MError::::MarketDoesNotExist ); }); @@ -1502,12 +1533,13 @@ fn check_appealable_market_fails_if_dispute_mechanism_wrong() { ExtBuilder::default().build().execute_with(|| { let now = >::block_number(); let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); + let market_id = >::get(court_id).unwrap(); MarketCommons::mutate_market(&market_id, |market| { market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; Ok(()) @@ -1515,7 +1547,7 @@ fn check_appealable_market_fails_if_dispute_mechanism_wrong() { .unwrap(); assert_noop!( - Court::check_appealable_market(&0, &court, now), + Court::check_appealable_market(court_id, &court, now), Error::::MarketDoesNotHaveCourtMechanism ); }); @@ -1526,14 +1558,14 @@ fn check_appealable_market_fails_if_not_in_appeal_period() { ExtBuilder::default().build().execute_with(|| { let now = >::block_number(); let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get()); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); assert_noop!( - Court::check_appealable_market(&0, &court, now), + Court::check_appealable_market(court_id, &court, now), Error::::NotInAppealPeriod ); }); @@ -1543,17 +1575,19 @@ fn check_appealable_market_fails_if_not_in_appeal_period() { fn appeal_last_appeal_just_removes_auto_resolve() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + fill_appeals(court_id, (MaxAppeals::get() - 1) as usize); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let resolve_at = court.cycle_ends.appeal; + + let market_id = >::get(court_id).unwrap(); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![]); }); @@ -1563,19 +1597,19 @@ fn appeal_last_appeal_just_removes_auto_resolve() { fn appeal_adds_last_appeal() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - fill_appeals(&market_id, (MaxAppeals::get() - 1) as usize); + fill_appeals(court_id, (MaxAppeals::get() - 1) as usize); - let last_draws = >::get(market_id); + let last_draws = >::get(court_id); let appealed_vote_item = - Court::get_latest_resolved_outcome(&market_id, last_draws.as_slice()).unwrap(); + Court::get_latest_winner_vote_item(court_id, last_draws.as_slice()).unwrap(); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); assert!(court.appeals.is_full()); let last_appeal = court.appeals.last().unwrap(); @@ -1587,7 +1621,7 @@ fn appeal_adds_last_appeal() { fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); @@ -1600,6 +1634,8 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { let salt = ::Hash::default(); let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let vote_item = VoteItem::Outcome(outcome); + let draws: crate::SelectedDrawsOf = vec![ Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() }, Draw { @@ -1611,21 +1647,21 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: 3 * MinJurorStake::get(), }, Draw { juror: DAVE, weight: 1, vote: Vote::Drawn, slashable: 4 * MinJurorStake::get() }, Draw { juror: EVE, weight: 1, - vote: Vote::Denounced { commitment, vote_item: outcome, salt }, + vote: Vote::Denounced { commitment, vote_item, salt }, slashable: 5 * MinJurorStake::get(), }, ] .try_into() .unwrap(); let old_draws = draws.clone(); - >::insert(market_id, draws); + >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -1633,6 +1669,7 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, ); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap(); @@ -1642,7 +1679,7 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { let free_dave_before = Balances::free_balance(&DAVE); let free_eve_before = Balances::free_balance(&EVE); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); let free_alice_after = Balances::free_balance(&ALICE); assert_ne!(free_alice_after, free_alice_before); @@ -1683,15 +1720,16 @@ fn reassign_juror_stakes_fails_if_court_not_found() { fn reassign_juror_stakes_emits_event() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); - System::assert_last_event(Event::JurorStakesReassigned { market_id }.into()); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + System::assert_last_event(Event::JurorStakesReassigned { court_id }.into()); }); } @@ -1699,17 +1737,18 @@ fn reassign_juror_stakes_emits_event() { fn reassign_juror_stakes_fails_if_juror_stakes_already_reassigned() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); assert_noop!( - Court::reassign_juror_stakes(Origin::signed(EVE), market_id), + Court::reassign_juror_stakes(Origin::signed(EVE), court_id), Error::::CourtAlreadyReassigned ); }); @@ -1719,19 +1758,21 @@ fn reassign_juror_stakes_fails_if_juror_stakes_already_reassigned() { fn reassign_juror_stakes_updates_court_status() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); - let court = >::get(market_id).unwrap(); - assert_eq!(court.status, CourtStatus::Closed { winner: resolution_outcome }); + let court = >::get(court_id).unwrap(); + let resolution_vote_item = VoteItem::Outcome(resolution_outcome); + assert_eq!(court.status, CourtStatus::Closed { winner: resolution_vote_item }); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); assert_eq!(court.status, CourtStatus::Reassigned); }); } @@ -1740,19 +1781,20 @@ fn reassign_juror_stakes_updates_court_status() { fn reassign_juror_stakes_removes_draws() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); - let draws = >::get(market_id); + let draws = >::get(court_id); assert!(!draws.is_empty()); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); - let draws = >::get(market_id); + let draws = >::get(court_id); assert!(draws.is_empty()); }); } @@ -1760,9 +1802,9 @@ fn reassign_juror_stakes_removes_draws() { #[test] fn reassign_juror_stakes_fails_if_court_not_closed() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); assert_noop!( - Court::reassign_juror_stakes(Origin::signed(EVE), market_id), + Court::reassign_juror_stakes(Origin::signed(EVE), court_id), Error::::CourtNotClosed ); }); @@ -1772,7 +1814,7 @@ fn reassign_juror_stakes_fails_if_court_not_closed() { fn reassign_juror_stakes_decreases_active_lock() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); @@ -1781,8 +1823,9 @@ fn reassign_juror_stakes_decreases_active_lock() { assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); let outcome = OutcomeReport::Scalar(42u128); + let vote_item = VoteItem::Outcome(outcome.clone()); let salt = ::Hash::default(); - let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); let alice_slashable = MinJurorStake::get(); >::mutate(ALICE, |juror_info| { @@ -1820,19 +1863,19 @@ fn reassign_juror_stakes_decreases_active_lock() { Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: charlie_slashable, }, Draw { juror: DAVE, weight: 1, - vote: Vote::Denounced { commitment, vote_item: outcome, salt }, + vote: Vote::Denounced { commitment, vote_item, salt }, slashable: dave_slashable, }, ] .try_into() .unwrap(); - >::insert(market_id, draws); + >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -1840,10 +1883,11 @@ fn reassign_juror_stakes_decreases_active_lock() { CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, ); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); assert!(>::get(ALICE).unwrap().active_lock.is_zero()); assert!(>::get(BOB).unwrap().active_lock.is_zero()); assert!(>::get(CHARLIE).unwrap().active_lock.is_zero()); @@ -1855,7 +1899,7 @@ fn reassign_juror_stakes_decreases_active_lock() { fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); @@ -1864,11 +1908,14 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); let outcome = OutcomeReport::Scalar(42u128); + let vote_item = VoteItem::Outcome(outcome.clone()); let salt = ::Hash::default(); - let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, vote_item.clone(), salt)); let wrong_outcome_0 = OutcomeReport::Scalar(69u128); + let wrong_vote_item_0 = VoteItem::Outcome(wrong_outcome_0); let wrong_outcome_1 = OutcomeReport::Scalar(56u128); + let wrong_vote_item_1 = VoteItem::Outcome(wrong_outcome_1); let alice_slashable = MinJurorStake::get(); let bob_slashable = 2 * MinJurorStake::get(); @@ -1879,32 +1926,32 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: alice_slashable, }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { commitment, vote_item: wrong_outcome_0, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_0, salt }, slashable: bob_slashable, }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item, salt }, slashable: charlie_slashable, }, Draw { juror: DAVE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: wrong_outcome_1, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_1, salt }, slashable: dave_slashable, }, ] .try_into() .unwrap(); let last_draws = draws.clone(); - >::insert(market_id, draws); + >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -1912,6 +1959,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, ); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); assert_eq!(resolution_outcome, outcome); @@ -1921,11 +1969,11 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let free_charlie_before = Balances::free_balance(CHARLIE); let free_dave_before = Balances::free_balance(DAVE); - let reward_pot = Court::reward_pot(&market_id); + let reward_pot = Court::reward_pot(court_id); let tardy_or_denounced_value = 5 * MinJurorStake::get(); let _ = Balances::deposit(&reward_pot, tardy_or_denounced_value).unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); let bob_slashed = last_draws[BOB as usize].slashable; let dave_slashed = last_draws[DAVE as usize].slashable; @@ -1955,7 +2003,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { fn reassign_juror_stakes_works_for_delegations() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); @@ -1965,10 +2013,12 @@ fn reassign_juror_stakes_works_for_delegations() { assert_ok!(Court::join_court(Origin::signed(EVE), amount)); let outcome = OutcomeReport::Scalar(42u128); + let vote_item = VoteItem::Outcome(outcome.clone()); let salt = ::Hash::default(); - let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, vote_item.clone(), salt)); let wrong_outcome = OutcomeReport::Scalar(69u128); + let wrong_vote_item = VoteItem::Outcome(wrong_outcome); let alice_slashable = MinJurorStake::get(); let bob_slashable = 2 * MinJurorStake::get(); @@ -1990,19 +2040,19 @@ fn reassign_juror_stakes_works_for_delegations() { Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: alice_slashable, }, Draw { juror: EVE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: outcome.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item, salt }, slashable: eve_slashable, }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { commitment, vote_item: wrong_outcome, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_vote_item, salt }, slashable: bob_slashable, }, Draw { @@ -2021,7 +2071,7 @@ fn reassign_juror_stakes_works_for_delegations() { .try_into() .unwrap(); let last_draws = draws.clone(); - >::insert(market_id, draws); + >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -2029,6 +2079,7 @@ fn reassign_juror_stakes_works_for_delegations() { CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, ); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let resolution_outcome = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); assert_eq!(resolution_outcome, outcome); @@ -2039,11 +2090,11 @@ fn reassign_juror_stakes_works_for_delegations() { let free_dave_before = Balances::free_balance(DAVE); let free_eve_before = Balances::free_balance(EVE); - let reward_pot = Court::reward_pot(&market_id); + let reward_pot = Court::reward_pot(court_id); let tardy_or_denounced_value = 5 * MinJurorStake::get(); let _ = Balances::deposit(&reward_pot, tardy_or_denounced_value).unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); let bob_slashed = last_draws.iter().find(|draw| draw.juror == BOB).unwrap().slashable; let charlie_delegated_bob_slashed = @@ -2098,7 +2149,7 @@ fn reassign_juror_stakes_works_for_delegations() { fn reassign_juror_stakes_rewards_treasury_if_no_winner() { ExtBuilder::default().build().execute_with(|| { fill_juror_pool(); - let market_id = initialize_court(); + let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); @@ -2107,42 +2158,45 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { assert_ok!(Court::join_court(Origin::signed(DAVE), amount)); let outcome = OutcomeReport::Scalar(42u128); + let vote_item = VoteItem::Outcome(outcome); let salt = ::Hash::default(); - let commitment = BlakeTwo256::hash_of(&(ALICE, outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(ALICE, vote_item.clone(), salt)); let wrong_outcome_0 = OutcomeReport::Scalar(69u128); + let wrong_vote_item_0 = VoteItem::Outcome(wrong_outcome_0); let wrong_outcome_1 = OutcomeReport::Scalar(56u128); + let wrong_vote_item_1 = VoteItem::Outcome(wrong_outcome_1); let draws: crate::SelectedDrawsOf = vec![ Draw { juror: ALICE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: wrong_outcome_1.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_1.clone(), salt }, slashable: MinJurorStake::get(), }, Draw { juror: BOB, weight: 1, - vote: Vote::Revealed { commitment, vote_item: wrong_outcome_0.clone(), salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_0.clone(), salt }, slashable: 2 * MinJurorStake::get(), }, Draw { juror: CHARLIE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: wrong_outcome_0, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_0, salt }, slashable: 3 * MinJurorStake::get(), }, Draw { juror: DAVE, weight: 1, - vote: Vote::Revealed { commitment, vote_item: wrong_outcome_1, salt }, + vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_1, salt }, slashable: 4 * MinJurorStake::get(), }, ] .try_into() .unwrap(); let last_draws = draws.clone(); - >::insert(market_id, draws); + >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -2150,9 +2204,9 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, ); - let mut court = >::get(market_id).unwrap(); - court.status = CourtStatus::Closed { winner: outcome }; - >::insert(market_id, court); + let mut court = >::get(court_id).unwrap(); + court.status = CourtStatus::Closed { winner: vote_item }; + >::insert(court_id, court); let free_alice_before = Balances::free_balance(ALICE); let free_bob_before = Balances::free_balance(BOB); @@ -2162,7 +2216,7 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { let treasury_account = Court::treasury_account_id(); let free_treasury_before = Balances::free_balance(&treasury_account); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), market_id)); + assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); let alice_slashed = last_draws[ALICE as usize].slashable; let bob_slashed = last_draws[BOB as usize].slashable; @@ -2202,13 +2256,14 @@ fn on_dispute_denies_non_court_markets() { #[test] fn on_resolution_sets_court_status() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); assert_eq!(market.report.as_ref().unwrap().outcome, ORACLE_REPORT); assert_eq!(Court::on_resolution(&market_id, &market).unwrap().result, Some(ORACLE_REPORT)); - let court = >::get(market_id).unwrap(); - assert_eq!(court.status, CourtStatus::Closed { winner: ORACLE_REPORT }); + let court = >::get(court_id).unwrap(); + assert_eq!(court.status, CourtStatus::Closed { winner: VoteItem::Outcome(ORACLE_REPORT) }); }); } @@ -2218,6 +2273,7 @@ fn on_resolution_fails_if_court_not_found() { let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); + >::insert(market_id, 0); assert_noop!(Court::on_resolution(&market_id, &market), Error::::CourtNotFound); }); } @@ -2249,13 +2305,14 @@ fn exchange_fails_if_non_court_markets() { #[test] fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let resolved_outcome = OutcomeReport::Scalar(1); let other_outcome = OutcomeReport::Scalar(2); - let mut court = >::get(market_id).unwrap(); + let mut court = >::get(court_id).unwrap(); let mut free_balances_before = BTreeMap::new(); let mut number = 0u128; let mut slashed_bonds = >::zero(); @@ -2266,9 +2323,9 @@ fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { // because the appealed outcomes are equal to the resolved outcome. // it is punished to appeal the right outcome slashed_bonds += bond; - resolved_outcome.clone() + VoteItem::Outcome(resolved_outcome.clone()) } else { - other_outcome.clone() + VoteItem::Outcome(other_outcome.clone()) }; let backer = number; @@ -2279,7 +2336,7 @@ fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { court.appeals.try_push(AppealInfo { backer, bond, appealed_vote_item }).unwrap(); number += 1; } - Courts::::insert(market_id, court); + Courts::::insert(court_id, court); let imbalance: NegativeImbalanceOf = as Currency>>::issue( @@ -2290,14 +2347,16 @@ fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { Court::exchange(&market_id, &market, &resolved_outcome, imbalance).unwrap(); assert_eq!(imb_remainder.result.peek(), prev_balance + slashed_bonds); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let appeals = court.appeals; for AppealInfo { backer, bond, appealed_vote_item } in appeals { assert_eq!(Balances::reserved_balance_named(&Court::reserve_id(), &backer), 0); let free_balance_after = Balances::free_balance(&backer); let free_balance_before = free_balances_before.get(&backer).unwrap(); - if appealed_vote_item == resolved_outcome { + let resolved_vote_item = VoteItem::Outcome(resolved_outcome.clone()); + + if appealed_vote_item == resolved_vote_item { assert_eq!(free_balance_after, *free_balance_before); } else { assert_eq!(free_balance_after, *free_balance_before + bond); @@ -2309,9 +2368,10 @@ fn exchange_slashes_unjustified_and_unreserves_justified_appealers() { #[test] fn get_auto_resolve_works() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let appeal_end = court.cycle_ends.appeal; assert_eq!(Court::get_auto_resolve(&market_id, &market).unwrap().result, Some(appeal_end)); }); @@ -2332,22 +2392,24 @@ fn get_auto_resolve_fails_if_wrong_dispute_mechanism() { #[test] fn on_global_dispute_removes_court() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); - assert!(>::contains_key(market_id)); + assert!(>::contains_key(court_id)); assert_ok!(Court::on_global_dispute(&market_id, &market)); - assert!(!>::contains_key(market_id)); + assert!(!>::contains_key(court_id)); }); } #[test] fn on_global_dispute_removes_draws() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); - assert!(>::contains_key(market_id)); + assert!(>::contains_key(court_id)); assert_ok!(Court::on_global_dispute(&market_id, &market)); - assert!(!>::contains_key(market_id)); + assert!(!>::contains_key(court_id)); }); } @@ -2366,6 +2428,7 @@ fn on_global_dispute_fails_if_wrong_dispute_mechanism() { #[test] fn on_global_dispute_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { + >::insert(0, 0); let market = DEFAULT_MARKET; assert_noop!(Court::on_global_dispute(&0, &market), Error::::CourtNotFound); }); @@ -2374,7 +2437,8 @@ fn on_global_dispute_fails_if_court_not_found() { #[test] fn on_global_dispute_fails_if_market_report_not_found() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); MarketCommons::mutate_market(&market_id, |market| { market.report = None; Ok(()) @@ -2391,26 +2455,27 @@ fn on_global_dispute_fails_if_market_report_not_found() { #[test] fn on_global_dispute_returns_appealed_outcomes() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); - let mut court = >::get(market_id).unwrap(); + let mut court = >::get(court_id).unwrap(); let mut gd_outcomes = Vec::new(); let initial_vote_amount = >::zero(); let treasury_account = Court::treasury_account_id(); for number in 0..MaxAppeals::get() { - let appealed_vote_item: ::VoteItem = - OutcomeReport::Scalar(number as u128); + let appealed_vote_item: VoteItem = + VoteItem::Outcome(OutcomeReport::Scalar(number as u128)); let backer = number as u128; let bond = crate::get_appeal_bond::(court.appeals.len()); gd_outcomes.push(GlobalDisputeItem { - outcome: appealed_vote_item.clone(), + outcome: appealed_vote_item.clone().into_outcome().unwrap(), owner: treasury_account, initial_vote_amount, }); court.appeals.try_push(AppealInfo { backer, bond, appealed_vote_item }).unwrap(); } - Courts::::insert(market_id, court); + Courts::::insert(court_id, court); assert_eq!(Court::on_global_dispute(&market_id, &market).unwrap().result, gd_outcomes); }); } @@ -2436,11 +2501,11 @@ fn choose_multiple_weighted_works() { #[test] fn select_jurors_updates_juror_consumed_stake() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); fill_juror_pool(); // the last appeal is reserved for global dispute backing let appeal_number = (MaxAppeals::get() - 1) as usize; - fill_appeals(&market_id, appeal_number); + fill_appeals(court_id, appeal_number); let jurors = JurorPool::::get(); let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); @@ -2477,9 +2542,9 @@ fn select_jurors_fails_if_not_enough_jurors(appeal_number: usize) { fn appeal_reduces_active_lock_from_old_draws() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); - let (market_id, _, _) = set_alice_after_vote(outcome); + let (court_id, _, _) = set_alice_after_vote(outcome); - let old_draws = >::get(market_id); + let old_draws = >::get(court_id); assert!(!old_draws.is_empty()); old_draws.iter().for_each(|draw| { let juror = draw.juror; @@ -2490,9 +2555,9 @@ fn appeal_reduces_active_lock_from_old_draws() { run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); - assert_ok!(Court::appeal(Origin::signed(CHARLIE), market_id)); + assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let new_draws = >::get(market_id); + let new_draws = >::get(court_id); old_draws.iter().for_each(|draw| { let juror = draw.juror; let juror_info = >::get(juror).unwrap(); @@ -2508,8 +2573,8 @@ fn appeal_reduces_active_lock_from_old_draws() { #[test] fn on_dispute_creates_correct_court_info() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - let court = >::get(market_id).unwrap(); + let court_id = initialize_court(); + let court = >::get(court_id).unwrap(); let cycle_ends = court.cycle_ends; let request_block = >::get(); assert_eq!(cycle_ends.pre_vote, request_block); @@ -2521,20 +2586,11 @@ fn on_dispute_creates_correct_court_info() { }); } -#[test] -fn on_dispute_fails_if_court_already_exists() { - ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - let market = MarketCommons::market(&market_id).unwrap(); - assert_noop!(Court::on_dispute(&market_id, &market), Error::::CourtAlreadyExists); - }); -} - #[test] fn on_dispute_inserts_draws() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - let draws = >::get(market_id); + let court_id = initialize_court(); + let draws = >::get(court_id); assert_eq!( draws[0], Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } @@ -2558,9 +2614,10 @@ fn on_dispute_inserts_draws() { #[test] fn on_dispute_adds_auto_resolve() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - let court = >::get(market_id).unwrap(); + let court_id = initialize_court(); + let court = >::get(court_id).unwrap(); let resolve_at = court.cycle_ends.appeal; + let market_id = >::get(court_id).unwrap(); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); }); } @@ -2568,11 +2625,12 @@ fn on_dispute_adds_auto_resolve() { #[test] fn has_failed_returns_true_for_appealable_court_too_few_jurors() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); // force empty jurors pool >::kill(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); - let court = >::get(market_id).unwrap(); + let court = >::get(court_id).unwrap(); let aggregation = court.cycle_ends.aggregation; run_to_block(aggregation + 1); assert!(Court::has_failed(&market_id, &market).unwrap().result); @@ -2582,10 +2640,11 @@ fn has_failed_returns_true_for_appealable_court_too_few_jurors() { #[test] fn has_failed_returns_true_for_appealable_court_appeals_full() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); - fill_appeals(&market_id, MaxAppeals::get() as usize); + fill_appeals(court_id, MaxAppeals::get() as usize); assert!(Court::has_failed(&market_id, &market).unwrap().result); }); @@ -2606,6 +2665,7 @@ fn has_failed_returns_true_for_uninitialized_court() { let market = MarketCommons::market(&market_id).unwrap(); let block_after_dispute_duration = report_block + market.deadlines.dispute_duration; run_to_block(block_after_dispute_duration - 1); + >::insert(market_id, 0); assert!(Court::has_failed(&market_id, &market).unwrap().result); }); } @@ -2631,14 +2691,14 @@ fn check_appeal_bond() { }); } -fn prepare_draws(market_id: &MarketIdOf, outcomes_with_weights: Vec<(u128, u32)>) { +fn prepare_draws(court_id: CourtId, outcomes_with_weights: Vec<(u128, u32)>) { let mut draws: crate::SelectedDrawsOf = vec![].try_into().unwrap(); for (i, (outcome_index, weight)) in outcomes_with_weights.iter().enumerate() { // offset to not conflict with other jurors let offset_i = (i + 1000) as u128; let juror = offset_i as u128; let salt = BlakeTwo256::hash_of(&offset_i); - let vote_item: ::VoteItem = OutcomeReport::Scalar(*outcome_index); + let vote_item: VoteItem = VoteItem::Outcome(OutcomeReport::Scalar(*outcome_index)); let commitment = BlakeTwo256::hash_of(&(juror, vote_item.clone(), salt)); draws .try_push(Draw { @@ -2649,50 +2709,50 @@ fn prepare_draws(market_id: &MarketIdOf, outcomes_with_weights: Vec<(u1 }) .unwrap(); } - >::insert(market_id, draws); + >::insert(court_id, draws); } #[test] fn get_winner_works() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); let outcomes_and_weights = vec![(1000u128, 8), (1001u128, 5), (1002u128, 42), (1003u128, 13)]; - prepare_draws(&market_id, outcomes_and_weights); + prepare_draws(court_id, outcomes_and_weights); - let draws = >::get(market_id); + let draws = >::get(court_id); let winner = Court::get_winner(draws.as_slice(), None).unwrap(); - assert_eq!(winner, OutcomeReport::Scalar(1002u128)); + assert_eq!(winner.into_outcome().unwrap(), OutcomeReport::Scalar(1002u128)); let outcomes_and_weights = vec![(1000u128, 2), (1000u128, 4), (1001u128, 4), (1001u128, 3)]; - prepare_draws(&market_id, outcomes_and_weights); + prepare_draws(court_id, outcomes_and_weights); - let draws = >::get(market_id); + let draws = >::get(court_id); let winner = Court::get_winner(draws.as_slice(), None).unwrap(); - assert_eq!(winner, OutcomeReport::Scalar(1001u128)); + assert_eq!(winner.into_outcome().unwrap(), OutcomeReport::Scalar(1001u128)); }); } #[test] fn get_winner_returns_none_for_no_revealed_draws() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - let draws = >::get(market_id); + let court_id = initialize_court(); + let draws = >::get(court_id); let winner = Court::get_winner(draws.as_slice(), None); assert_eq!(winner, None); }); } #[test] -fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { +fn get_latest_winner_vote_item_selects_last_appealed_outcome_for_tie() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); - let mut court = >::get(market_id).unwrap(); + let court_id = initialize_court(); + let mut court = >::get(court_id).unwrap(); // create a tie of two best outcomes let weights = vec![(1000u128, 42), (1001u128, 42)]; - let appealed_vote_item: ::VoteItem = - OutcomeReport::Scalar(weights.len() as u128); - prepare_draws(&market_id, weights); + let appealed_vote_item: VoteItem = + VoteItem::Outcome(OutcomeReport::Scalar(weights.len() as u128)); + prepare_draws(court_id, weights); court .appeals .try_push(AppealInfo { @@ -2701,24 +2761,28 @@ fn get_latest_resolved_outcome_selects_last_appealed_outcome_for_tie() { appealed_vote_item: appealed_vote_item.clone(), }) .unwrap(); - >::insert(market_id, court); + >::insert(court_id, court); - let draws = >::get(market_id); - let latest = Court::get_latest_resolved_outcome(&market_id, draws.as_slice()).unwrap(); + let draws = >::get(court_id); + let latest = Court::get_latest_winner_vote_item(court_id, draws.as_slice()).unwrap(); assert_eq!(latest, appealed_vote_item); - assert!(latest != ORACLE_REPORT); + assert!(latest.into_outcome().unwrap() != ORACLE_REPORT); }); } #[test] -fn get_latest_resolved_outcome_selects_oracle_report() { +fn get_latest_winner_vote_item_selects_oracle_report() { ExtBuilder::default().build().execute_with(|| { - let market_id = initialize_court(); + let court_id = initialize_court(); + let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); assert_eq!(market.report.unwrap().outcome, ORACLE_REPORT); - let draws = >::get(market_id); + let draws = >::get(court_id); assert_eq!( - Court::get_latest_resolved_outcome(&market_id, draws.as_slice()).unwrap(), + Court::get_latest_winner_vote_item(court_id, draws.as_slice()) + .unwrap() + .into_outcome() + .unwrap(), ORACLE_REPORT ); }); diff --git a/zrml/court/src/traits.rs b/zrml/court/src/traits.rs index aa1bb4af2..65cfaaa67 100644 --- a/zrml/court/src/traits.rs +++ b/zrml/court/src/traits.rs @@ -1,5 +1,21 @@ use frame_support::dispatch::DispatchError; -pub trait VoteValidation { - fn pre_validate() -> Result<(), DispatchError>; -} \ No newline at end of file +use crate::types::VoteItem; + +pub trait VoteCheckApi { + type MarketId; + + fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), DispatchError>; +} + +pub trait AppealCheckApi { + type MarketId; + + fn pre_appeal(market_id: &Self::MarketId) -> Result<(), DispatchError>; +} + +pub trait DefaultWinnerApi { + type MarketId; + + fn default_winner(market_id: &Self::MarketId) -> Result; +} diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index dc45231e2..8ae81d4c2 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -16,6 +16,60 @@ // along with Zeitgeist. If not, see . extern crate alloc; use alloc::{vec, vec::Vec}; +use zeitgeist_primitives::types::OutcomeReport; + +pub type CourtId = u128; + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub enum CourtVoteItem { + Outcome, + Binary, +} + +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, + Ord, + PartialOrd, +)] +pub enum VoteItem { + Outcome(OutcomeReport), + Binary(bool), +} + +impl VoteItem { + pub fn into_outcome(self) -> Option { + match self { + Self::Outcome(report) => Some(report), + _ => None, + } + } +} + +impl VoteItem { + pub fn is_outcome(&self) -> bool { + matches!(self, Self::Outcome(_)) + } + + pub fn is_binary(&self) -> bool { + matches!(self, Self::Binary(_)) + } +} /// The general information about a particular juror. #[derive( @@ -40,7 +94,7 @@ pub struct JurorInfo { } /// The raw information behind the secret hash of a juror's vote. -pub struct RawCommitment { +pub struct RawCommitment { /// The juror's account id. pub juror: AccountId, /// The vote item which the juror voted for. @@ -50,11 +104,11 @@ pub struct RawCommitment { } /// The raw information which is hashed to create the secret hash of a juror's vote. -pub struct CommitmentMatcher { +pub struct CommitmentMatcher { /// The juror's hashed commitment pub hashed: Hash, /// The raw commitment which is intended to lead to the hashed commitment. - pub raw: RawCommitment, + pub raw: RawCommitment, } /// All possible states of a vote. @@ -68,7 +122,7 @@ pub struct CommitmentMatcher { PartialEq, Eq, )] -pub enum Vote { +pub enum Vote { /// The juror delegated stake to other jurors. Delegated { delegated_stakes: DelegatedStakes }, /// The juror was randomly selected to vote in a specific court case. @@ -114,7 +168,7 @@ pub struct CycleEnds { PartialEq, Eq, )] -pub enum CourtStatus { +pub enum CourtStatus { /// The court case has been started. Open, /// The court case was closed, the winner vote item was determined. @@ -134,7 +188,7 @@ pub enum CourtStatus { PartialEq, Eq, )] -pub struct AppealInfo { +pub struct AppealInfo { /// The account which made the appeal. pub backer: AccountId, /// The amount of funds which were locked for the appeal. @@ -154,13 +208,14 @@ pub struct AppealInfo { PartialEq, Eq, )] -pub struct CourtInfo { +pub struct CourtInfo { /// The status of the court case. - pub status: CourtStatus, + pub status: CourtStatus, /// The list of all appeals. pub appeals: Appeals, /// The information about the lifecycle of this court case. pub cycle_ends: CycleEnds, + pub court_vote_item: CourtVoteItem, } /// The timing information about a court case. @@ -175,17 +230,17 @@ pub struct RoundTiming { pub appeal_period: BlockNumber, } -impl - CourtInfo +impl + CourtInfo { - pub fn new(round_timing: RoundTiming) -> Self { + pub fn new(round_timing: RoundTiming, court_vote_item: CourtVoteItem) -> Self { let pre_vote = round_timing.pre_vote_end; let vote = pre_vote.saturating_add(round_timing.vote_period); let aggregation = vote.saturating_add(round_timing.aggregation_period); let appeal = aggregation.saturating_add(round_timing.appeal_period); let cycle_ends = CycleEnds { pre_vote, vote, aggregation, appeal }; let status = CourtStatus::Open; - Self { status, appeals: Default::default(), cycle_ends } + Self { status, appeals: Default::default(), cycle_ends, court_vote_item } } pub fn update_lifecycle(&mut self, round_timing: RoundTiming) { @@ -210,14 +265,14 @@ impl { +pub struct Draw { /// The juror who was randomly selected. pub juror: AccountId, /// The weight of the juror in this court case. /// The higher the weight the more voice the juror has in the final winner decision. pub weight: u32, /// The information about the vote state. - pub vote: Vote, + pub vote: Vote, /// The amount of funds which can be slashed for this court case. /// This is related to a multiple of `MinStake` to mitigate Sybil attacks. pub slashable: Balance, @@ -276,13 +331,13 @@ pub enum SelectionAdd { DelegationWeight, } -pub struct SelfInfo { +pub struct SelfInfo { pub slashable: Balance, pub vote_item: VoteItem, } -pub struct JurorVoteWithStakes { - pub self_info: Option>, +pub struct JurorVoteWithStakes { + pub self_info: Option>, // many delegators can have delegated to the same juror // that's why the value is a vector and should be sorted (binary search by key) // the key is the delegator account @@ -290,7 +345,7 @@ pub struct JurorVoteWithStakes { pub delegations: Vec<(AccountId, Balance)>, } -impl Default for JurorVoteWithStakes { +impl Default for JurorVoteWithStakes { fn default() -> Self { JurorVoteWithStakes { self_info: None, delegations: vec![] } } diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index aba85eef2..6282f7ca1 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -24,7 +24,9 @@ use crate as prediction_markets; use frame_support::{ - construct_runtime, ord_parameter_types, parameter_types, + construct_runtime, ensure, ord_parameter_types, + pallet_prelude::DispatchError, + parameter_types, traits::{Everything, NeverEnsureOrigin, OnFinalize, OnInitialize}, }; use frame_system::{EnsureRoot, EnsureSignedBy}; @@ -52,10 +54,16 @@ use zeitgeist_primitives::{ }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, - CurrencyId, Hash, Index, MarketId, Moment, PoolId, SerdeWrapper, UncheckedExtrinsicTest, - VoteItem, + CurrencyId, Hash, Index, MarketDisputeMechanism, MarketId, MarketStatus, Moment, PoolId, + SerdeWrapper, UncheckedExtrinsicTest, }, }; +use zrml_court::{ + traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, + types::VoteItem, + Error as CError, +}; +use zrml_market_commons::MarketCommonsPalletApi; use zeitgeist_primitives::constants::mock::{ GlobalDisputeLockId, GlobalDisputePeriod, GlobalDisputesPalletId, MaxGlobalDisputeVotes, @@ -248,8 +256,50 @@ impl zrml_authorized::Config for Runtime { type WeightInfo = zrml_authorized::weights::WeightInfo; } +pub struct AppealCheck; +impl AppealCheckApi for AppealCheck { + type MarketId = MarketId; + + fn pre_appeal(market_id: &Self::MarketId) -> Result<(), DispatchError> { + let market = MarketCommons::market(market_id)?; + ensure!(market.status == MarketStatus::Disputed, CError::::MarketIsNotDisputed); + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + CError::::MarketDoesNotHaveCourtMechanism + ); + Ok(()) + } +} + +pub struct VoteCheck; +impl VoteCheckApi for VoteCheck { + type MarketId = MarketId; + + fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), DispatchError> { + let market = MarketCommons::market(market_id)?; + ensure!( + market.matches_outcome_report(&vote_item.into_outcome().unwrap()), + CError::::OutcomeMismatch + ); + Ok(()) + } +} + +pub struct DefaultWinner; +impl DefaultWinnerApi for DefaultWinner { + type MarketId = MarketId; + + fn default_winner(market_id: &Self::MarketId) -> Result { + let market = MarketCommons::market(market_id)?; + let report = market.report.as_ref().ok_or(CError::::MarketReportNotFound)?; + let vote_item = VoteItem::Outcome(report.outcome.clone()); + Ok(vote_item) + } +} + impl zrml_court::Config for Runtime { type AppealBond = AppealBond; + type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; type DisputeResolution = prediction_markets::Pallet; type CourtVotePeriod = CourtVotePeriod; @@ -257,6 +307,7 @@ impl zrml_court::Config for Runtime { type CourtAppealPeriod = CourtAppealPeriod; type CourtLockId = CourtLockId; type Currency = Balances; + type DefaultWinner = DefaultWinner; type Event = Event; type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; @@ -271,7 +322,7 @@ impl zrml_court::Config for Runtime { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; - type VoteItem = VoteItem; + type VoteCheck = VoteCheck; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index ef55f0f2a..c1c8eefaf 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3178,25 +3178,27 @@ fn it_resolves_a_disputed_court_market() { // outcome_0 is the plurality decision => right outcome let outcome_0 = OutcomeReport::Categorical(0); + let vote_item_0 = VoteItem::Outcome(outcome_0.clone()); // outcome_1 is the wrong outcome let outcome_1 = OutcomeReport::Categorical(1); + let vote_item_1 = VoteItem::Outcome(outcome_1); - let commitment_0 = BlakeTwo256::hash_of(&(juror_0, outcome_0.clone(), salt)); + let commitment_0 = BlakeTwo256::hash_of(&(juror_0, vote_item_0.clone(), salt)); assert_ok!(Court::vote(Origin::signed(juror_0), market_id, commitment_0)); // juror_1 votes for non-plurality outcome => slashed later - let commitment_1 = BlakeTwo256::hash_of(&(juror_1, outcome_1.clone(), salt)); + let commitment_1 = BlakeTwo256::hash_of(&(juror_1, vote_item_1.clone(), salt)); assert_ok!(Court::vote(Origin::signed(juror_1), market_id, commitment_1)); - let commitment_2 = BlakeTwo256::hash_of(&(juror_2, outcome_0.clone(), salt)); + let commitment_2 = BlakeTwo256::hash_of(&(juror_2, vote_item_0.clone(), salt)); assert_ok!(Court::vote(Origin::signed(juror_2), market_id, commitment_2)); - let commitment_3 = BlakeTwo256::hash_of(&(juror_3, outcome_0.clone(), salt)); + let commitment_3 = BlakeTwo256::hash_of(&(juror_3, vote_item_0.clone(), salt)); assert_ok!(Court::vote(Origin::signed(juror_3), market_id, commitment_3)); // juror_4 fails to vote in time - let commitment_5 = BlakeTwo256::hash_of(&(juror_5, outcome_0.clone(), salt)); + let commitment_5 = BlakeTwo256::hash_of(&(juror_5, vote_item_0.clone(), salt)); assert_ok!(Court::vote(Origin::signed(juror_5), market_id, commitment_5)); // juror_3 is denounced by juror_0 => slashed later @@ -3204,30 +3206,40 @@ fn it_resolves_a_disputed_court_market() { Origin::signed(juror_0), market_id, juror_3, - outcome_0.clone(), + vote_item_0.clone(), salt )); let aggregation_start = court.cycle_ends.vote + 1; run_to_block(aggregation_start); - assert_ok!(Court::reveal_vote(Origin::signed(juror_0), market_id, outcome_0.clone(), salt)); - assert_ok!(Court::reveal_vote(Origin::signed(juror_1), market_id, outcome_1, salt)); + assert_ok!(Court::reveal_vote( + Origin::signed(juror_0), + market_id, + vote_item_0.clone(), + salt + )); + assert_ok!(Court::reveal_vote(Origin::signed(juror_1), market_id, vote_item_1, salt)); let wrong_salt = BlakeTwo256::hash_of(&69); assert_noop!( - Court::reveal_vote(Origin::signed(juror_2), market_id, outcome_0.clone(), wrong_salt), + Court::reveal_vote(Origin::signed(juror_2), market_id, vote_item_0.clone(), wrong_salt), CError::::InvalidReveal ); - assert_ok!(Court::reveal_vote(Origin::signed(juror_2), market_id, outcome_0.clone(), salt)); + assert_ok!(Court::reveal_vote( + Origin::signed(juror_2), + market_id, + vote_item_0.clone(), + salt + )); assert_noop!( - Court::reveal_vote(Origin::signed(juror_3), market_id, outcome_0.clone(), salt), + Court::reveal_vote(Origin::signed(juror_3), market_id, vote_item_0.clone(), salt), CError::::VoteAlreadyDenounced ); assert_noop!( - Court::reveal_vote(Origin::signed(juror_4), market_id, outcome_0.clone(), salt), + Court::reveal_vote(Origin::signed(juror_4), market_id, vote_item_0.clone(), salt), CError::::JurorNotVoted ); @@ -3241,9 +3253,9 @@ fn it_resolves_a_disputed_court_market() { let market_after = MarketCommons::market(&0).unwrap(); assert_eq!(market_after.status, MarketStatus::Resolved); - assert_eq!(market_after.resolved_outcome, Some(outcome_0.clone())); + assert_eq!(market_after.resolved_outcome, Some(outcome_0)); let court_after = zrml_court::Courts::::get(market_id).unwrap(); - assert_eq!(court_after.status, CourtStatus::Closed { winner: outcome_0 }); + assert_eq!(court_after.status, CourtStatus::Closed { winner: vote_item_0 }); let free_juror_0_before = Balances::free_balance(&juror_0); let free_juror_1_before = Balances::free_balance(&juror_1); @@ -3311,10 +3323,11 @@ fn simulate_appeal_cycle(market_id: MarketId) { let salt = ::Hash::default(); let wrong_outcome = OutcomeReport::Categorical(1); + let wrong_vote_item = VoteItem::Outcome(wrong_outcome.clone()); let draws = zrml_court::SelectedDraws::::get(market_id); for draw in &draws { - let commitment = BlakeTwo256::hash_of(&(draw.juror, wrong_outcome.clone(), salt)); + let commitment = BlakeTwo256::hash_of(&(draw.juror, wrong_vote_item.clone(), salt)); assert_ok!(Court::vote(Origin::signed(draw.juror), market_id, commitment)); } @@ -3325,7 +3338,7 @@ fn simulate_appeal_cycle(market_id: MarketId) { assert_ok!(Court::reveal_vote( Origin::signed(draw.juror), market_id, - wrong_outcome.clone(), + wrong_vote_item.clone(), salt, )); } From 1c6d5b5cc25f01093f8767e515c995e306b80761 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 26 Apr 2023 14:57:41 +0200 Subject: [PATCH 276/368] fix stuff and add documentation --- zrml/court/src/lib.rs | 25 +++++++++++++++++++++++++ zrml/court/src/types.rs | 23 +++++++++++++++++++++-- zrml/prediction-markets/src/tests.rs | 2 +- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 4ea55dbe2..bdd500996 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -95,8 +95,10 @@ mod pallet { #[pallet::constant] type AppealBond: Get>; + /// The functionality to check an appeal beforehand. type AppealCheck: AppealCheckApi>; + /// The expected blocks per year to calculate the inflation emission. #[pallet::constant] type BlocksPerYear: Get; @@ -125,6 +127,7 @@ mod pallet { + NamedReservableCurrency + LockableCurrency; + /// The functionality to get a default winner if no juror voted inside a court. type DefaultWinner: DefaultWinnerApi>; /// The functionality to allow controlling the markets resolution time. @@ -138,6 +141,7 @@ mod pallet { /// Event type Event: From> + IsType<::Event>; + /// The inflation period in which new tokens are minted. #[pallet::constant] type InflationPeriod: Get; @@ -156,6 +160,7 @@ mod pallet { #[pallet::constant] type MaxSelectedDraws: Get; + /// The maximum number of possible delegations. #[pallet::constant] type MaxDelegations: Get; @@ -184,6 +189,7 @@ mod pallet { #[pallet::constant] type TreasuryPalletId: Get; + /// The functionality to check a vote item beforehand. type VoteCheck: VoteCheckApi>; /// Weights generated by benchmarks @@ -262,13 +268,16 @@ mod pallet { #[pallet::storage] pub type Courts = StorageMap<_, Blake2_128Concat, CourtId, CourtOf, OptionQuery>; + /// The next identifier for a new court. #[pallet::storage] pub type NextCourtId = StorageValue<_, CourtId, ValueQuery>; + /// Mapping from market id to court id. #[pallet::storage] pub type MarketIdToCourtId = StorageMap<_, Twox64Concat, MarketIdOf, CourtId, OptionQuery>; + /// Mapping from court id to market id. #[pallet::storage] pub type CourtIdToMarketId = StorageMap<_, Twox64Concat, CourtId, MarketIdOf, OptionQuery>; @@ -1056,6 +1065,16 @@ mod pallet { Ok(Some(T::WeightInfo::reassign_juror_stakes(draws_len)).into()) } + /// Set the yearly inflation rate of the court system. + /// This is only allowed to be called by the `MonetaryGovernanceOrigin`. + /// + /// # Arguments + /// + /// - `inflation`: The desired yearly inflation rate. + /// + /// # Weight + /// + /// Complexity: `O(1)` #[pallet::weight(T::WeightInfo::set_inflation())] #[transactional] pub fn set_inflation(origin: OriginFor, inflation: Perbill) -> DispatchResult { @@ -1192,6 +1211,7 @@ mod pallet { false } + /// Add a delegated juror to the `delegated_stakes` vector. fn add_delegated_juror( mut delegated_stakes: DelegatedStakesOf, delegated_juror: &T::AccountId, @@ -1284,6 +1304,8 @@ mod pallet { } } + /// Return the first valid active juror starting + /// from the `random_number` index out of the `delegations`. fn get_valid_delegated_juror( delegations: &[T::AccountId], random_number: u128, @@ -1321,6 +1343,7 @@ mod pallet { delegated_juror } + /// Add a juror with her stake to the `selections` map. fn add_to_selections( selections: &mut BTreeMap>, juror: &T::AccountId, @@ -1586,6 +1609,8 @@ mod pallet { T::TreasuryPalletId::get().into_account_truncating() } + /// The court has a specific vote item type. + /// We ensure that the vote item matches the predefined vote item type. pub(crate) fn check_vote_item( court: &CourtOf, vote_item: &VoteItem, diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 8ae81d4c2..bae3f1d12 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -18,8 +18,10 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use zeitgeist_primitives::types::OutcomeReport; +/// The type of the court identifier. pub type CourtId = u128; +/// The different court vote types. This can be extended to allow different decision making options. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -35,6 +37,8 @@ pub enum CourtVoteItem { Binary, } +/// The different court vote types with their raw values. +/// This can be extended to allow different decision making options. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -52,6 +56,7 @@ pub enum VoteItem { Binary(bool), } +/// Simple implementations to handle vote items easily. impl VoteItem { pub fn into_outcome(self) -> Option { match self { @@ -59,9 +64,7 @@ impl VoteItem { _ => None, } } -} -impl VoteItem { pub fn is_outcome(&self) -> bool { matches!(self, Self::Outcome(_)) } @@ -299,6 +302,7 @@ pub struct JurorPoolItem { pub consumed_stake: Balance, } +/// The information about an internal selected draw of a juror or delegator. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -310,11 +314,16 @@ pub struct JurorPoolItem { Eq, )] pub struct SelectionValue { + /// The overall weight of the juror or delegator for a specific selected draw. pub weight: u32, + /// The amount that can be slashed for this selected draw. pub slashable: Balance, + /// The different portions of stake distributed over multiple jurors. + /// The sum of all delegated stakes should be equal to `slashable`. pub delegated_stakes: DelegatedStakes, } +/// The type to add one weight to the selected draws. #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -326,17 +335,26 @@ pub struct SelectionValue { Eq, )] pub enum SelectionAdd { + /// The variant to add an active juror, who is not a delegator. SelfStake { lock: Balance }, + /// The variant to decide that a delegator is added + /// to the selected draws and locks stake on a delegated juror. DelegationStake { delegated_juror: AccountId, lock: Balance }, + /// The variant to know that one weight for the delegation to the delegated juror is added. DelegationWeight, } +/// The information about an active juror who voted for a court. pub struct SelfInfo { + /// The slashable amount of the juror herself. pub slashable: Balance, + /// The item for which the juror voted. pub vote_item: VoteItem, } pub struct JurorVoteWithStakes { + /// An optional information about an active juror herself, who was selected and voted. + /// This could be None, because delegators could have delegated to a juror who failed to vote. pub self_info: Option>, // many delegators can have delegated to the same juror // that's why the value is a vector and should be sorted (binary search by key) @@ -351,6 +369,7 @@ impl Default for JurorVoteWithStakes { } } +/// An internal error type to determine how the selection of draws fails. pub enum SelectionError { NoValidDelegatedJuror, } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index c1c8eefaf..2c7032f37 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3323,7 +3323,7 @@ fn simulate_appeal_cycle(market_id: MarketId) { let salt = ::Hash::default(); let wrong_outcome = OutcomeReport::Categorical(1); - let wrong_vote_item = VoteItem::Outcome(wrong_outcome.clone()); + let wrong_vote_item = VoteItem::Outcome(wrong_outcome); let draws = zrml_court::SelectedDraws::::get(market_id); for draw in &draws { From c34b8c83e595c320ec47c3782e1441ae5038bf56 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 28 Apr 2023 09:38:07 +0200 Subject: [PATCH 277/368] add court readme, use new parameters --- primitives/src/constants/mock.rs | 2 +- primitives/src/traits/dispute_api.rs | 2 +- runtime/battery-station/src/parameters.rs | 8 +- runtime/zeitgeist/src/parameters.rs | 8 +- zrml/authorized/src/benchmarks.rs | 2 +- zrml/authorized/src/lib.rs | 19 +-- zrml/authorized/src/tests.rs | 4 +- zrml/court/README.md | 54 ++++++++- zrml/court/src/benchmarks.rs | 31 +++-- zrml/court/src/lib.rs | 76 +++++------- zrml/court/src/mock.rs | 4 - zrml/court/src/tests.rs | 131 ++++++++------------ zrml/court/src/weights.rs | 140 ++++++++++++---------- zrml/prediction-markets/src/lib.rs | 10 +- zrml/prediction-markets/src/tests.rs | 8 ++ zrml/simple-disputes/src/benchmarks.rs | 2 +- zrml/simple-disputes/src/lib.rs | 15 ++- 17 files changed, 268 insertions(+), 248 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index e907c2158..969eb000b 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -45,7 +45,7 @@ parameter_types! { pub const CourtLockId: LockIdentifier = *b"zge/cloc"; pub const MaxAppeals: u32 = 4; pub const MaxDelegations: u32 = 5; - pub const MaxSelectedDraws: u32 = 94; + pub const MaxSelectedDraws: u32 = 510; pub const MaxJurors: u32 = 1_000; pub const MinJurorStake: Balance = 50 * CENT; pub const InflationPeriod: BlockNumber = 20; diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index 385bbd7de..b4ca908b8 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -98,7 +98,7 @@ pub trait DisputeApi { fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOfDisputeApi, - ) -> Result>, DispatchError>; + ) -> ResultWithWeightInfo>; /// Returns `true` if the market dispute mechanism /// was unable to come to a conclusion. diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 162ff2fb7..e68ade033 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -99,12 +99,12 @@ parameter_types! { pub const MaxAppeals: u32 = 4; /// The maximum number of delegations per juror account. pub const MaxDelegations: u32 = 5; - /// The maximum number of randomly selected jurors for a dispute. - pub const MaxSelectedDraws: u32 = 94; - /// The maximum number of jurors that can be registered. + /// The maximum number of randomly selected `MinJurorStake` draws / atoms of jurors for a dispute. + pub const MaxSelectedDraws: u32 = 510; + /// The maximum number of jurors / delegators that can be registered. pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. - pub const MinJurorStake: Balance = 1000 * BASE; + pub const MinJurorStake: Balance = 500 * BASE; /// The interval for requesting multiple court votes at once. pub const RequestInterval: BlockNumber = 7 * BLOCKS_PER_DAY; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index ce916846f..90b58dc46 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -99,12 +99,12 @@ parameter_types! { pub const MaxAppeals: u32 = 4; /// The maximum number of delegations per juror account. pub const MaxDelegations: u32 = 5; - /// The maximum number of randomly selected jurors for a dispute. - pub const MaxSelectedDraws: u32 = 94; - /// The maximum number of jurors that can be registered. + /// The maximum number of randomly selected `MinJurorStake` draws / atoms of jurors for a dispute. + pub const MaxSelectedDraws: u32 = 510; + /// The maximum number of jurors / delegators that can be registered. pub const MaxJurors: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. - pub const MinJurorStake: Balance = 1000 * BASE; + pub const MinJurorStake: Balance = 500 * BASE; /// The interval for requesting multiple court votes at once. pub const RequestInterval: BlockNumber = 7 * BLOCKS_PER_DAY; diff --git a/zrml/authorized/src/benchmarks.rs b/zrml/authorized/src/benchmarks.rs index 1ddf58cf8..afddad4dd 100644 --- a/zrml/authorized/src/benchmarks.rs +++ b/zrml/authorized/src/benchmarks.rs @@ -134,7 +134,7 @@ benchmarks! { let report = AuthorityReport { resolve_at: 0u32.into(), outcome: OutcomeReport::Scalar(0) }; AuthorizedOutcomeReports::::insert(market_id, report); }: { - Authorized::::get_auto_resolve(&market_id, &market).unwrap(); + Authorized::::get_auto_resolve(&market_id, &market); } has_failed_weight { diff --git a/zrml/authorized/src/lib.rs b/zrml/authorized/src/lib.rs index 675061058..7f2c4bb0c 100644 --- a/zrml/authorized/src/lib.rs +++ b/zrml/authorized/src/lib.rs @@ -296,18 +296,19 @@ mod pallet { fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result>, DispatchError> { - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Authorized, - Error::::MarketDoesNotHaveDisputeMechanismAuthorized - ); - - let res = ResultWithWeightInfo { - result: Self::get_auto_resolve(market_id), + ) -> ResultWithWeightInfo> { + let mut res = ResultWithWeightInfo { + result: None, weight: T::WeightInfo::get_auto_resolve_weight(), }; - Ok(res) + if market.dispute_mechanism != MarketDisputeMechanism::Authorized { + return res; + } + + res.result = Self::get_auto_resolve(market_id); + + res } fn has_failed( diff --git a/zrml/authorized/src/tests.rs b/zrml/authorized/src/tests.rs index 515e656e2..683d6bc62 100644 --- a/zrml/authorized/src/tests.rs +++ b/zrml/authorized/src/tests.rs @@ -244,7 +244,7 @@ fn get_auto_resolve_works() { )); let now = frame_system::Pallet::::block_number(); let resolve_at = now + ::CorrectionPeriod::get(); - assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap().result, Some(resolve_at),); + assert_eq!(Authorized::get_auto_resolve(&0, &market).result, Some(resolve_at),); }); } @@ -252,6 +252,6 @@ fn get_auto_resolve_works() { fn get_auto_resolve_returns_none_without_market_storage() { ExtBuilder::default().build().execute_with(|| { let market = market_mock::(); - assert_eq!(Authorized::get_auto_resolve(&0, &market).unwrap().result, None,); + assert_eq!(Authorized::get_auto_resolve(&0, &market).result, None,); }); } diff --git a/zrml/court/README.md b/zrml/court/README.md index 53bbf35ec..07d2e6fc8 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -1 +1,53 @@ -# Court Module +# Court + +A pallet for stake-weighted, plurality decision making. + +## Overview + +The court system is responsible for ensuring that the truth is added to the blockchain. +Prediction markets, which depend on accurate information, +reward traders who base their decisions on truthful data. +If someone provides false information, they will be punished, +while those who share accurate information will be rewarded. + +## Terminology + +- `aggregation period` - The period in which the actively participating jurors + need to reveal their vote secrets. + +## Interface + +### Dispatches + +#### Public Dispatches + +- `join_court` - Join the court with a stake to become a juror in order to get the stake-weighted + chance to be selected for decision making. +- `delegate` - Join the court with a stake to become a delegator in order to + delegate the voting power to actively participating jurors. +- `prepare_exit_court` - Prepare as a court participant to leave the court system. +- `exit_court` - Exit the court system in order to get the stake back. +- `vote` - An actively participating juror votes secretely on a specific court case, + in which the juror got selected. +- `denounce_vote` - Denounce a selected and active juror, if the secret and + vote is known before the actual reveal period. +- `reveal_vote` - An actively participating juror reveals the previously casted secret vote. +- `appeal` - After the reveal phase (aggregation period), the jurors decision can be appealed. +- `reassign_juror_stakes` - After the appeal period is over, + losers pay the winners for the jurors and delegators. + +#### `MonetaryGovernanceOrigin` Dispatches + +- `set_inflation` - Set the yearly inflation rate of the court system. + +#### Private Pallet API + +- `on_dispute` - Initialise a new court case. +- `on_resolution` - Resolve an existing court case and return the decision winner. +- `exchange` - Slash the unjustified appealers, put those funds to the treasury + and unreserve the justified appeal bonds. +- `get_auto_resolve` - Get the block number when `on_resolution` is evoked for a court case. +- `has_failed` - Determine whether the court mechanism failed + to meet the preconditions to start a court case. +- `on_global_dispute` - Prepare the global dispute if the court mechanism has failed. +- `clear` - Clean up the storage items of a specific court case. \ No newline at end of file diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index be044cc97..2f8b33519 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -408,14 +408,14 @@ benchmarks! { }: _(RawOrigin::Signed(caller), court_id, vote_item, salt) appeal { - // from 47 because in the last appeal round we need at least 47 jurors - let j in 47..T::MaxJurors::get(); + // from 255 because in the last appeal round we need at least 255 jurors + let j in 255..T::MaxJurors::get(); let a in 0..(T::MaxAppeals::get() - 2); let r in 0..62; let f in 0..62; let necessary_jurors_weight = Court::::necessary_jurors_weight((T::MaxAppeals::get() - 1) as usize); - debug_assert!(necessary_jurors_weight == 47usize); + debug_assert!(necessary_jurors_weight == 255usize); fill_pool::(j)?; fill_delegations::(); @@ -487,6 +487,7 @@ benchmarks! { } reassign_juror_stakes { + // because we have 5 MaxDelegations let d in 5..T::MaxSelectedDraws::get(); debug_assert!(T::MaxDelegations::get() < T::MaxSelectedDraws::get()); @@ -511,7 +512,7 @@ benchmarks! { let mut draws = >::get(court_id); let mut delegated_stakes: DelegatedStakesOf = Default::default(); for i in 0..d { - let juror: T::AccountId = account("juror", i, 0); + let juror: T::AccountId = account("juror_i", i, 0); deposit::(&juror); >::insert(&juror, JurorInfo { stake: T::MinJurorStake::get(), @@ -571,7 +572,7 @@ benchmarks! { } on_dispute { - let j in 5..T::MaxJurors::get(); + let j in 31..T::MaxJurors::get(); let r in 0..62; let now = >::block_number(); @@ -600,7 +601,8 @@ benchmarks! { on_resolution { let d in 1..T::MaxSelectedDraws::get(); - fill_pool::(5)?; + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); @@ -627,7 +629,8 @@ benchmarks! { exchange { let a in 0..T::MaxAppeals::get(); - fill_pool::(5)?; + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); @@ -652,15 +655,17 @@ benchmarks! { } get_auto_resolve { - fill_pool::(5)?; + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); }: { - Court::::get_auto_resolve(&market_id, &market).unwrap(); + Court::::get_auto_resolve(&market_id, &market); } has_failed { - fill_pool::(5)?; + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); }: { @@ -671,7 +676,8 @@ benchmarks! { let a in 0..T::MaxAppeals::get(); let d in 1..T::MaxSelectedDraws::get(); - fill_pool::(5)?; + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); @@ -701,7 +707,8 @@ benchmarks! { clear { let d in 1..T::MaxSelectedDraws::get(); - fill_pool::(5)?; + let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); + fill_pool::(necessary_jurors_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bdd500996..65934f986 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -197,7 +197,7 @@ mod pallet { } // Number of draws for an initial market dispute. - const INITIAL_DRAWS_NUM: usize = 5; + const INITIAL_DRAWS_NUM: usize = 31; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); // Weight used to increase the number of jurors for subsequent appeals @@ -304,26 +304,13 @@ mod pallet { T: Config, { /// A juror has been added to the court. - JurorJoined { - juror: T::AccountId, - stake: BalanceOf, - }, + JurorJoined { juror: T::AccountId, stake: BalanceOf }, /// A juror prepared to exit the court. - JurorPreparedExit { - juror: T::AccountId, - }, + JurorPreparedExit { juror: T::AccountId }, /// A juror has been removed from the court. - JurorExited { - juror: T::AccountId, - exit_amount: BalanceOf, - active_lock: BalanceOf, - }, + JurorExited { juror: T::AccountId, exit_amount: BalanceOf, active_lock: BalanceOf }, /// A juror has voted in a court. - JurorVoted { - court_id: CourtId, - juror: T::AccountId, - commitment: T::Hash, - }, + JurorVoted { court_id: CourtId, juror: T::AccountId, commitment: T::Hash }, /// A juror has revealed their vote. JurorRevealedVote { juror: T::AccountId, @@ -346,21 +333,14 @@ mod pallet { delegated_jurors: Vec, }, /// A market has been appealed. - CourtAppealed { - court_id: CourtId, - appeal_number: u32, - }, - MintedInCourt { - juror: T::AccountId, - amount: BalanceOf, - }, + CourtAppealed { court_id: CourtId, appeal_number: u32 }, + /// A new token amount was minted for the court jurors. + MintedInCourt { juror: T::AccountId, amount: BalanceOf }, /// The juror stakes have been reassigned. The losing jurors have been slashed. /// The winning jurors have been rewarded by the losers. /// The losing jurors are those, who did not vote, /// were denounced or did not reveal their vote. - JurorStakesReassigned { - court_id: CourtId, - }, + JurorStakesReassigned { court_id: CourtId }, } #[pallet::error] @@ -1482,6 +1462,9 @@ mod pallet { ) -> Result>, DispatchError> { let mut jurors = >::get(); let total_weight = Self::get_unconsumed_stake_sum(jurors.as_slice()); + let required_weight = + (number as u128).saturating_mul(T::MinJurorStake::get().saturated_into::()); + ensure!(total_weight >= required_weight, Error::::NotEnoughJurors); let random_set = Self::get_n_random_numbers(number, total_weight)?; let selections = Self::get_selections(&mut jurors, random_set); >::put(jurors); @@ -1520,10 +1503,7 @@ mod pallet { pub(crate) fn select_jurors( appeal_number: usize, ) -> Result, DispatchError> { - let jurors_len = >::decode_len().unwrap_or(0); let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); - ensure!(jurors_len >= necessary_jurors_weight, Error::::NotEnoughJurors); - let random_jurors = Self::choose_multiple_weighted(necessary_jurors_weight)?; // keep in mind that the number of draws is at maximum necessary_jurors_weight * 2 @@ -1654,9 +1634,9 @@ mod pallet { } // Calculates the necessary number of draws depending on the number of market appeals. - pub(crate) fn necessary_jurors_weight(appeals_len: usize) -> usize { - // 2^(appeals_len) * 5 + 2^(appeals_len) - 1 - // MaxAppeals - 1 (= 3) example: 2^3 * 5 + 2^3 - 1 = 47 + pub fn necessary_jurors_weight(appeals_len: usize) -> usize { + // 2^(appeals_len) * 31 + 2^(appeals_len) - 1 + // MaxAppeals - 1 (= 3) example: 2^3 * 31 + 2^3 - 1 = 255 APPEAL_BASIS .saturating_pow(appeals_len as u32) .saturating_mul(INITIAL_DRAWS_NUM) @@ -1873,7 +1853,7 @@ mod pallet { } fn on_resolution_max_weight() -> Weight { - T::WeightInfo::on_resolution(T::MaxJurors::get(), T::MaxSelectedDraws::get()) + T::WeightInfo::on_resolution(T::MaxSelectedDraws::get()) } fn exchange_max_weight() -> Weight { @@ -1981,7 +1961,7 @@ mod pallet { let res = ResultWithWeightInfo { result: Some(winner_outcome), - weight: T::WeightInfo::on_resolution(T::MaxJurors::get(), draws_len), + weight: T::WeightInfo::on_resolution(draws_len), }; Ok(res) @@ -2029,21 +2009,19 @@ mod pallet { fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result>, DispatchError> { - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Court, - Error::::MarketDoesNotHaveCourtMechanism - ); + ) -> ResultWithWeightInfo> { + let mut res = + ResultWithWeightInfo { result: None, weight: T::WeightInfo::get_auto_resolve() }; - let court_id = >::get(market_id) - .ok_or(Error::::MarketIdToCourtIdNotFound)?; + if market.dispute_mechanism != MarketDisputeMechanism::Court { + return res; + } - let res = ResultWithWeightInfo { - result: >::get(court_id).map(|court| court.cycle_ends.appeal), - weight: T::WeightInfo::get_auto_resolve(), - }; + if let Some(court_id) = >::get(market_id) { + res.result = >::get(court_id).map(|court| court.cycle_ends.appeal); + } - Ok(res) + res } fn has_failed( diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 47b8dd688..72330c4ec 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -56,10 +56,6 @@ pub const BOB: AccountIdTest = 1; pub const CHARLIE: AccountIdTest = 2; pub const DAVE: AccountIdTest = 3; pub const EVE: AccountIdTest = 4; -pub const FERDIE: AccountIdTest = 5; -pub const GINA: AccountIdTest = 6; -pub const HARRY: AccountIdTest = 7; -pub const IAN: AccountIdTest = 8; pub const POOR_PAUL: AccountIdTest = 9; pub const INITIAL_BALANCE: u128 = 1000 * BASE; pub const SUDO: AccountIdTest = 69; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index c0b594fd0..c86a4196b 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -22,8 +22,7 @@ extern crate alloc; use crate::{ mock::{ run_blocks, run_to_block, Balances, Court, ExtBuilder, MarketCommons, Origin, Runtime, - System, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, GINA, HARRY, IAN, INITIAL_BALANCE, - POOR_PAUL, + System, ALICE, BOB, CHARLIE, DAVE, EVE, INITIAL_BALANCE, POOR_PAUL, }, mock_storage::pallet::MarketIdsPerDisputeBlock, types::{CourtStatus, Draw, Vote, VoteItem}, @@ -103,8 +102,8 @@ fn initialize_court() -> CourtId { >::get(market_id).unwrap() } -fn fill_juror_pool() { - for i in 0..MaxJurors::get() { +fn fill_juror_pool(jurors_len: u32) { + for i in 0..jurors_len { let amount = MinJurorStake::get() + i as u128; let juror = (i + 1000) as u128; let _ = Balances::deposit(&juror, amount).unwrap(); @@ -152,7 +151,7 @@ fn put_alice_in_draw(court_id: CourtId, stake: BalanceOf) { fn set_alice_after_vote( outcome: OutcomeReport, ) -> (CourtId, ::Hash, ::Hash) { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -170,18 +169,6 @@ fn set_alice_after_vote( (court_id, commitment, salt) } -const DEFAULT_SET_OF_JURORS: &[JurorPoolItem] = &[ - JurorPoolItem { stake: 9, juror: HARRY, consumed_stake: 0 }, - JurorPoolItem { stake: 8, juror: IAN, consumed_stake: 0 }, - JurorPoolItem { stake: 7, juror: ALICE, consumed_stake: 0 }, - JurorPoolItem { stake: 6, juror: BOB, consumed_stake: 0 }, - JurorPoolItem { stake: 5, juror: CHARLIE, consumed_stake: 0 }, - JurorPoolItem { stake: 4, juror: DAVE, consumed_stake: 0 }, - JurorPoolItem { stake: 3, juror: EVE, consumed_stake: 0 }, - JurorPoolItem { stake: 2, juror: FERDIE, consumed_stake: 0 }, - JurorPoolItem { stake: 1, juror: GINA, consumed_stake: 0 }, -]; - fn the_lock(amount: u128) -> BalanceLock { BalanceLock { id: CourtLockId::get(), amount, reasons: pallet_balances::Reasons::All } } @@ -640,7 +627,7 @@ fn exit_court_fails_if_inflation_period_not_over() { #[test] fn vote_works() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -648,7 +635,7 @@ fn vote_works() { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(court_id); - assert_eq!(draws.len(), 5usize); + assert_eq!(draws.len(), Court::necessary_jurors_weight(0usize)); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); @@ -693,7 +680,7 @@ fn vote_works() { #[test] fn vote_overwrite_works() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -755,7 +742,7 @@ fn vote_fails_if_vote_state_incorrect( vote: crate::Vote<::Hash, crate::DelegatedStakesOf>, ) { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -781,7 +768,7 @@ fn vote_fails_if_vote_state_incorrect( #[test] fn vote_fails_if_caller_not_in_draws() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let mut draws = >::get(court_id); @@ -803,7 +790,7 @@ fn vote_fails_if_caller_not_in_draws() { #[test] fn vote_fails_if_not_in_voting_period() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -826,7 +813,7 @@ fn vote_fails_if_not_in_voting_period() { #[test] fn reveal_vote_works() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -834,7 +821,7 @@ fn reveal_vote_works() { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(court_id); - assert_eq!(draws.len(), 5usize); + assert_eq!(draws.len(), Court::necessary_jurors_weight(0usize)); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); @@ -1620,7 +1607,7 @@ fn appeal_adds_last_appeal() { #[test] fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -1813,7 +1800,7 @@ fn reassign_juror_stakes_fails_if_court_not_closed() { #[test] fn reassign_juror_stakes_decreases_active_lock() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -1898,7 +1885,7 @@ fn reassign_juror_stakes_decreases_active_lock() { #[test] fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -2002,7 +1989,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { #[test] fn reassign_juror_stakes_works_for_delegations() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -2148,7 +2135,7 @@ fn reassign_juror_stakes_works_for_delegations() { #[test] fn reassign_juror_stakes_rewards_treasury_if_no_winner() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -2373,19 +2360,7 @@ fn get_auto_resolve_works() { let market = MarketCommons::market(&market_id).unwrap(); let court = >::get(court_id).unwrap(); let appeal_end = court.cycle_ends.appeal; - assert_eq!(Court::get_auto_resolve(&market_id, &market).unwrap().result, Some(appeal_end)); - }); -} - -#[test] -fn get_auto_resolve_fails_if_wrong_dispute_mechanism() { - ExtBuilder::default().build().execute_with(|| { - let mut market = DEFAULT_MARKET; - market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; - assert_noop!( - Court::get_auto_resolve(&0, &market), - Error::::MarketDoesNotHaveCourtMechanism - ); + assert_eq!(Court::get_auto_resolve(&market_id, &market).result, Some(appeal_end)); }); } @@ -2483,7 +2458,7 @@ fn on_global_dispute_returns_appealed_outcomes() { #[test] fn choose_multiple_weighted_works() { ExtBuilder::default().build().execute_with(|| { - let necessary_jurors_weight = Court::necessary_jurors_weight(5usize); + let necessary_jurors_weight = Court::necessary_jurors_weight(0usize); for i in 0..necessary_jurors_weight { let amount = MinJurorStake::get() + i as u128; let juror = i as u128; @@ -2502,7 +2477,7 @@ fn choose_multiple_weighted_works() { fn select_jurors_updates_juror_consumed_stake() { ExtBuilder::default().build().execute_with(|| { let court_id = initialize_court(); - fill_juror_pool(); + fill_juror_pool(MaxJurors::get()); // the last appeal is reserved for global dispute backing let appeal_number = (MaxAppeals::get() - 1) as usize; fill_appeals(court_id, appeal_number); @@ -2593,21 +2568,35 @@ fn on_dispute_inserts_draws() { let draws = >::get(court_id); assert_eq!( draws[0], - Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } + Draw { + juror: ALICE, + weight: 3, + vote: Vote::Drawn, + slashable: 3 * MinJurorStake::get() + } ); assert_eq!( draws[1], - Draw { juror: BOB, weight: 2, vote: Vote::Drawn, slashable: 2 * MinJurorStake::get() } + Draw { juror: BOB, weight: 4, vote: Vote::Drawn, slashable: 4 * MinJurorStake::get() } ); assert_eq!( draws[2], - Draw { juror: DAVE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } + Draw { + juror: CHARLIE, + weight: 8, + vote: Vote::Drawn, + slashable: 8 * MinJurorStake::get() + } ); assert_eq!( draws[3], - Draw { juror: EVE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() } + Draw { juror: DAVE, weight: 8, vote: Vote::Drawn, slashable: 8 * MinJurorStake::get() } + ); + assert_eq!( + draws[4], + Draw { juror: EVE, weight: 8, vote: Vote::Drawn, slashable: 8 * MinJurorStake::get() } ); - assert_eq!(draws.len(), 4usize); + assert_eq!(draws.len(), 5usize); }); } @@ -2673,10 +2662,10 @@ fn has_failed_returns_true_for_uninitialized_court() { #[test] fn check_necessary_jurors_weight() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(Court::necessary_jurors_weight(0usize), 5usize); - assert_eq!(Court::necessary_jurors_weight(1usize), 11usize); - assert_eq!(Court::necessary_jurors_weight(2usize), 23usize); - assert_eq!(Court::necessary_jurors_weight(3usize), 47usize); + assert_eq!(Court::necessary_jurors_weight(0usize), 31usize); + assert_eq!(Court::necessary_jurors_weight(1usize), 63usize); + assert_eq!(Court::necessary_jurors_weight(2usize), 127usize); + assert_eq!(Court::necessary_jurors_weight(3usize), 255usize); }); } @@ -2793,20 +2782,7 @@ fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { ExtBuilder::default().build().execute_with(|| { run_to_block(123); - let mut jurors = >::get(); - for pool_item in DEFAULT_SET_OF_JURORS.iter() { - >::insert( - pool_item.juror, - JurorInfo { - stake: pool_item.stake, - active_lock: 0u128, - prepare_exit_at: None, - delegations: Default::default(), - }, - ); - jurors.try_push(pool_item.clone()).unwrap(); - } - >::put(jurors); + fill_juror_pool(MaxJurors::get()); let nonce_0 = 42u64; >::put(nonce_0); @@ -2855,24 +2831,13 @@ fn get_random_seed_returns_equal_seeds_with_equal_nonce() { fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { run_to_block(123); - let mut jurors = >::get(); - for pool_item in DEFAULT_SET_OF_JURORS.iter() { - >::insert( - pool_item.juror, - JurorInfo { - stake: pool_item.stake, - active_lock: 0u128, - prepare_exit_at: None, - delegations: Default::default(), - }, - ); - jurors.try_push(pool_item.clone()).unwrap(); - } - >::put(jurors); + fill_juror_pool(MaxJurors::get()); + + let jurors = >::get(); let random_jurors = Court::choose_multiple_weighted(2).unwrap(); for draw in random_jurors { - assert!(DEFAULT_SET_OF_JURORS.iter().any(|el| el.juror == draw.juror)); + assert!(jurors.iter().any(|el| el.juror == draw.juror)); } }); } diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 89ce294dd..d0e8789e0 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-24, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-26, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -59,7 +59,7 @@ pub trait WeightInfoZeitgeist { fn handle_inflation(j: u32) -> Weight; fn select_jurors(a: u32) -> Weight; fn on_dispute(j: u32, r: u32) -> Weight; - fn on_resolution(j: u32, d: u32) -> Weight; + fn on_resolution(d: u32) -> Weight; fn exchange(a: u32) -> Weight; fn get_auto_resolve() -> Weight; fn has_failed() -> Weight; @@ -74,9 +74,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn join_court(j: u32) -> Weight { - Weight::from_ref_time(33_635_000) + Weight::from_ref_time(45_966_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(98_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(83_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -84,11 +84,11 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:6 w:1) // Storage: Balances Locks (r:1 w:1) fn delegate(j: u32, d: u32) -> Weight { - Weight::from_ref_time(48_104_000) + Weight::from_ref_time(41_432_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(122_000).saturating_mul(j.into())) - // Standard Error: 45_000 - .saturating_add(Weight::from_ref_time(447_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(134_000).saturating_mul(j.into())) + // Standard Error: 92_000 + .saturating_add(Weight::from_ref_time(1_358_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -96,9 +96,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) fn prepare_exit_court(j: u32) -> Weight { - Weight::from_ref_time(19_274_000) + Weight::from_ref_time(19_093_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(84_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(86_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -119,35 +119,36 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) fn vote(d: u32) -> Weight { - Weight::from_ref_time(49_081_000) + Weight::from_ref_time(50_448_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(106_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(99_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + // Storage: Court CourtIdToMarketId (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) fn denounce_vote(d: u32) -> Weight { - Weight::from_ref_time(39_078_000) + Weight::from_ref_time(42_693_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(124_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(Weight::from_ref_time(131_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } + // Storage: Court CourtIdToMarketId (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn reveal_vote(d: u32) -> Weight { - Weight::from_ref_time(66_728_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(91_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(4)) + fn reveal_vote(_d: u32) -> Weight { + Weight::from_ref_time(80_215_000) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court Courts (r:1 w:1) + // Storage: Court CourtIdToMarketId (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) @@ -159,11 +160,11 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Balances Reserves (r:1 w:1) fn appeal(j: u32, a: u32, _r: u32, _f: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(1_390_000).saturating_mul(j.into())) - // Standard Error: 1_348_000 - .saturating_add(Weight::from_ref_time(630_362_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(13)) + // Standard Error: 1_000 + .saturating_add(Weight::from_ref_time(1_377_000).saturating_mul(j.into())) + // Standard Error: 689_000 + .saturating_add(Weight::from_ref_time(638_909_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().reads((28_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(8)) .saturating_add(T::DbWeight::get().writes((28_u64).saturating_mul(a.into()))) @@ -174,8 +175,8 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: System Account (r:6 w:5) fn reassign_juror_stakes(d: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 8_000 - .saturating_add(Weight::from_ref_time(41_797_000).saturating_mul(d.into())) + // Standard Error: 13_000 + .saturating_add(Weight::from_ref_time(40_759_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -190,8 +191,8 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: System Account (r:1 w:1) fn handle_inflation(j: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 25_000 - .saturating_add(Weight::from_ref_time(14_531_000).saturating_mul(j.into())) + // Standard Error: 4_000 + .saturating_add(Weight::from_ref_time(13_078_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) @@ -199,17 +200,17 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:9 w:5) + // Storage: Court Jurors (r:125 w:120) fn select_jurors(a: u32) -> Weight { - Weight::from_ref_time(226_140_000) - // Standard Error: 2_247_000 - .saturating_add(Weight::from_ref_time(507_706_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().reads((12_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(4)) - .saturating_add(T::DbWeight::get().writes((12_u64).saturating_mul(a.into()))) + Weight::from_ref_time(2_518_341_000) + // Standard Error: 34_748_000 + .saturating_add(Weight::from_ref_time(7_367_178_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(103)) + .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(97)) + .saturating_add(T::DbWeight::get().writes((161_u64).saturating_mul(a.into()))) } - // Storage: Court Courts (r:1 w:1) + // Storage: Court NextCourtId (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) @@ -217,71 +218,82 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) // Storage: Court SelectedDraws (r:0 w:1) + // Storage: Court CourtIdToMarketId (r:0 w:1) + // Storage: Court MarketIdToCourtId (r:0 w:1) + // Storage: Court Courts (r:0 w:1) fn on_dispute(j: u32, r: u32) -> Weight { - Weight::from_ref_time(65_336_000) + Weight::from_ref_time(55_203_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(164_000).saturating_mul(j.into())) - // Standard Error: 10_000 - .saturating_add(Weight::from_ref_time(185_000).saturating_mul(r.into())) + .saturating_add(Weight::from_ref_time(148_000).saturating_mul(j.into())) + // Standard Error: 2_000 + .saturating_add(Weight::from_ref_time(18_000).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(11)) - .saturating_add(T::DbWeight::get().writes(10)) + .saturating_add(T::DbWeight::get().writes(13)) } + // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:1) // Storage: Court SelectedDraws (r:1 w:0) + // Storage: Court CourtIdToMarketId (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) - // Storage: Court Jurors (r:94 w:94) - fn on_resolution(j: u32, d: u32) -> Weight { - Weight::from_ref_time(0) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(99_000).saturating_mul(j.into())) + // Storage: Court Jurors (r:1 w:1) + fn on_resolution(d: u32) -> Weight { + Weight::from_ref_time(19_040_000) // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(4_737_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(Weight::from_ref_time(4_329_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) } + // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Balances Reserves (r:1 w:1) // Storage: System Account (r:1 w:1) fn exchange(a: u32) -> Weight { - Weight::from_ref_time(13_802_000) - // Standard Error: 30_000 - .saturating_add(Weight::from_ref_time(21_667_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(1)) + Weight::from_ref_time(18_796_000) + // Standard Error: 50_000 + .saturating_add(Weight::from_ref_time(20_739_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(a.into()))) } + // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:0) fn get_auto_resolve() -> Weight { - Weight::from_ref_time(7_000_000).saturating_add(T::DbWeight::get().reads(1)) + Weight::from_ref_time(8_000_000).saturating_add(T::DbWeight::get().reads(2)) } + // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court JurorPool (r:1 w:0) // Storage: Court Courts (r:1 w:0) + // Storage: Court CourtIdToMarketId (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) fn has_failed() -> Weight { - Weight::from_ref_time(17_000_000).saturating_add(T::DbWeight::get().reads(3)) + Weight::from_ref_time(23_000_000).saturating_add(T::DbWeight::get().reads(5)) } + // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:1) // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:94 w:94) - fn on_global_dispute(_a: u32, d: u32) -> Weight { - Weight::from_ref_time(12_713_000) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(4_069_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(2)) + fn on_global_dispute(a: u32, d: u32) -> Weight { + Weight::from_ref_time(11_100_000) + // Standard Error: 18_000 + .saturating_add(Weight::from_ref_time(766_000).saturating_mul(a.into())) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(4_017_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) } + // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:1 w:1) // Storage: Court Courts (r:0 w:1) fn clear(d: u32) -> Weight { - Weight::from_ref_time(9_761_000) + Weight::from_ref_time(12_162_000) // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(4_033_000).saturating_mul(d.into())) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(Weight::from_ref_time(3_963_000).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 2743f29bd..50293e2c6 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1433,9 +1433,7 @@ mod pallet { #[transactional] pub fn start_global_dispute( origin: OriginFor, - #[allow(dead_code, unused)] - #[pallet::compact] - market_id: MarketIdOf, + #[pallet::compact] market_id: MarketIdOf, ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; @@ -2216,13 +2214,13 @@ mod pallet { let ResultWithWeightInfo { result: auto_resolve_block_opt, weight: _ } = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { - T::Authorized::get_auto_resolve(market_id, &market)? + T::Authorized::get_auto_resolve(market_id, &market) } MarketDisputeMechanism::Court => { - T::Court::get_auto_resolve(market_id, &market)? + T::Court::get_auto_resolve(market_id, &market) } MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::get_auto_resolve(market_id, &market)? + T::SimpleDisputes::get_auto_resolve(market_id, &market) } }; if let Some(auto_resolve_block) = auto_resolve_block_opt { diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 2c7032f37..d53dc74b6 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3125,6 +3125,14 @@ fn it_resolves_a_disputed_court_market() { assert_ok!(Court::join_court(Origin::signed(*j), amount)); } + // just to have enough jurors for the dispute + for j in 1006..(1006 + Court::necessary_jurors_weight(0usize) as u32) { + let juror = j as u128; + let amount = MinJurorStake::get() + juror; + assert_ok!(AssetManager::deposit(Asset::Ztg, &juror, amount + SENTINEL_AMOUNT)); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } + let end = 2; assert_ok!(PredictionMarkets::create_market( Origin::signed(ALICE), diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index 6f64cc8d7..3fb5db8f4 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -129,7 +129,7 @@ benchmarks! { fill_disputes::(market_id, d); }: { - SimpleDisputes::::get_auto_resolve(&market_id, &market).unwrap(); + SimpleDisputes::::get_auto_resolve(&market_id, &market); } has_failed_weight { diff --git a/zrml/simple-disputes/src/lib.rs b/zrml/simple-disputes/src/lib.rs index aa60ff84b..fa05e3c78 100644 --- a/zrml/simple-disputes/src/lib.rs +++ b/zrml/simple-disputes/src/lib.rs @@ -428,11 +428,14 @@ mod pallet { fn get_auto_resolve( market_id: &Self::MarketId, market: &MarketOf, - ) -> Result>, DispatchError> { - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::SimpleDisputes, - Error::::MarketDoesNotHaveSimpleDisputesMechanism - ); + ) -> ResultWithWeightInfo> { + if market.dispute_mechanism != MarketDisputeMechanism::SimpleDisputes { + return ResultWithWeightInfo { + result: None, + weight: T::WeightInfo::get_auto_resolve_weight(T::MaxDisputes::get()), + }; + } + let disputes = Disputes::::get(market_id); let res = ResultWithWeightInfo { @@ -440,7 +443,7 @@ mod pallet { weight: T::WeightInfo::get_auto_resolve_weight(disputes.len() as u32), }; - Ok(res) + res } fn has_failed( From e8a086bcdf7feda45a1211cc54b26564b4ed683b Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 28 Apr 2023 13:00:43 +0200 Subject: [PATCH 278/368] improve naming, weighting --- zrml/court/src/benchmarks.rs | 38 ++++----- zrml/court/src/lib.rs | 116 ++++++++++++++------------ zrml/court/src/tests.rs | 32 ++++---- zrml/court/src/types.rs | 8 +- zrml/court/src/weights.rs | 118 +++++++++++++-------------- zrml/prediction-markets/src/tests.rs | 2 +- 6 files changed, 162 insertions(+), 152 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 2f8b33519..6372c8ae8 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -326,8 +326,8 @@ benchmarks! { denounce_vote { let d in 1..T::MaxSelectedDraws::get(); - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let caller: T::AccountId = whitelisted_caller(); let (market_id, court_id) = setup_court::()?; @@ -414,8 +414,8 @@ benchmarks! { let r in 0..62; let f in 0..62; - let necessary_jurors_weight = Court::::necessary_jurors_weight((T::MaxAppeals::get() - 1) as usize); - debug_assert!(necessary_jurors_weight == 255usize); + let necessary_draws_weight = Court::::necessary_draws_weight((T::MaxAppeals::get() - 1) as usize); + debug_assert!(necessary_draws_weight == 255usize); fill_pool::(j)?; fill_delegations::(); @@ -446,7 +446,7 @@ benchmarks! { // remove last random selections of on_dispute >::remove(court_id); let mut draws = >::get(court_id); - let draws_len = Court::::necessary_jurors_weight(a as usize) as u32; + let draws_len = Court::::necessary_draws_weight(a as usize) as u32; for i in 0..draws_len { let juror: T::AccountId = account("juror", i, 0); >::insert(&juror, JurorInfo { @@ -492,8 +492,8 @@ benchmarks! { debug_assert!(T::MaxDelegations::get() < T::MaxSelectedDraws::get()); // just to initialize the court - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let caller: T::AccountId = whitelisted_caller(); let (market_id, court_id) = setup_court::()?; @@ -601,8 +601,8 @@ benchmarks! { on_resolution { let d in 1..T::MaxSelectedDraws::get(); - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); @@ -629,8 +629,8 @@ benchmarks! { exchange { let a in 0..T::MaxAppeals::get(); - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); @@ -655,8 +655,8 @@ benchmarks! { } get_auto_resolve { - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); }: { @@ -664,8 +664,8 @@ benchmarks! { } has_failed { - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); }: { @@ -676,8 +676,8 @@ benchmarks! { let a in 0..T::MaxAppeals::get(); let d in 1..T::MaxSelectedDraws::get(); - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); @@ -707,8 +707,8 @@ benchmarks! { clear { let d in 1..T::MaxSelectedDraws::get(); - let necessary_jurors_weight: usize = Court::::necessary_jurors_weight(0usize); - fill_pool::(necessary_jurors_weight as u32)?; + let necessary_draws_weight: usize = Court::::necessary_draws_weight(0usize); + fill_pool::(necessary_draws_weight as u32)?; let (market_id, court_id) = setup_court::()?; let market = get_market::(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 65934f986..b2e797481 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -25,9 +25,9 @@ extern crate alloc; use crate::{ traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, weights::WeightInfoZeitgeist, - AppealInfo, CommitmentMatcher, CourtId, CourtInfo, CourtStatus, CourtVoteItem, Draw, JurorInfo, - JurorPoolItem, JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, SelectionError, - SelectionValue, SelfInfo, Vote, VoteItem, + AppealInfo, CommitmentMatcher, CourtId, CourtInfo, CourtStatus, Draw, JurorInfo, JurorPoolItem, + JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, SelectionError, SelectionValue, + SelfInfo, Vote, VoteItem, VoteItemType, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -46,7 +46,7 @@ use frame_support::{ NamedReservableCurrency, OnUnbalanced, Randomness, ReservableCurrency, StorageVersion, WithdrawReasons, }, - transactional, Blake2_128Concat, BoundedVec, PalletId, + transactional, Blake2_128Concat, BoundedVec, PalletId, Twox64Concat, }; use frame_system::{ ensure_signed, @@ -84,8 +84,6 @@ pub use types::*; #[frame_support::pallet] mod pallet { - use frame_support::Twox64Concat; - use super::*; #[pallet::config] @@ -239,7 +237,7 @@ mod pallet { pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; pub(crate) type CommitmentMatcherOf = CommitmentMatcher, HashOf>; pub(crate) type RawCommitmentOf = RawCommitment, HashOf>; - pub type CacheSize = ConstU32<64>; + pub(crate) type CacheSize = ConstU32<64>; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -341,6 +339,8 @@ mod pallet { /// The losing jurors are those, who did not vote, /// were denounced or did not reveal their vote. JurorStakesReassigned { court_id: CourtId }, + /// The yearly inflation rate has been set. + InflationSet { inflation: Perbill }, } #[pallet::error] @@ -391,7 +391,7 @@ mod pallet { /// The juror stakes of the court already got reassigned. CourtAlreadyReassigned, /// There are not enough jurors in the pool. - NotEnoughJurors, + NotEnoughJurorsStake, /// The report of the market was not found. MarketReportNotFound, /// The maximum number of court ids is reached. @@ -956,7 +956,7 @@ mod pallet { let winner = match court.status { CourtStatus::Closed { winner } => winner, CourtStatus::Reassigned => return Err(Error::::CourtAlreadyReassigned.into()), - _ => return Err(Error::::CourtNotClosed.into()), + CourtStatus::Open => return Err(Error::::CourtNotClosed.into()), }; let draws = SelectedDraws::::get(court_id); @@ -976,20 +976,7 @@ mod pallet { // map delegated jurors to own_slashable, vote item and Vec<(delegator, delegator_stake)> let mut jurors_to_stakes = BTreeMap::>::new(); - for draw in draws { - if let Some(mut juror_info) = >::get(&draw.juror) { - juror_info.active_lock = juror_info.active_lock.saturating_sub(draw.slashable); - >::insert(&draw.juror, juror_info); - } else { - log::warn!( - "Juror {:?} not found in Jurors storage (reassign_juror_stakes). Court id \ - {:?}.", - draw.juror, - court_id - ); - debug_assert!(false); - } - + let mut handle_vote = |draw: DrawOf| { match draw.vote { Vote::Drawn | Vote::Secret { commitment: _ } @@ -1031,6 +1018,23 @@ mod pallet { } } } + }; + + for draw in draws { + if let Some(mut juror_info) = >::get(&draw.juror) { + juror_info.active_lock = juror_info.active_lock.saturating_sub(draw.slashable); + >::insert(&draw.juror, juror_info); + } else { + log::warn!( + "Juror {:?} not found in Jurors storage (reassign_juror_stakes). Court id \ + {:?}.", + draw.juror, + court_id + ); + debug_assert!(false); + } + + handle_vote(draw); } Self::slash_losers_to_award_winners(court_id, jurors_to_stakes, &winner); @@ -1062,6 +1066,8 @@ mod pallet { >::put(inflation); + Self::deposit_event(Event::InflationSet { inflation }); + Ok(()) } } @@ -1458,14 +1464,14 @@ mod pallet { // according to the weighted stake of the jurors. // Return the random draws. pub(crate) fn choose_multiple_weighted( - number: usize, + draw_weight: usize, ) -> Result>, DispatchError> { let mut jurors = >::get(); - let total_weight = Self::get_unconsumed_stake_sum(jurors.as_slice()); - let required_weight = - (number as u128).saturating_mul(T::MinJurorStake::get().saturated_into::()); - ensure!(total_weight >= required_weight, Error::::NotEnoughJurors); - let random_set = Self::get_n_random_numbers(number, total_weight)?; + let jurors_unconsumed_stake = Self::get_unconsumed_stake_sum(jurors.as_slice()); + let required_stake = (draw_weight as u128) + .saturating_mul(T::MinJurorStake::get().saturated_into::()); + ensure!(jurors_unconsumed_stake >= required_stake, Error::::NotEnoughJurorsStake); + let random_set = Self::get_n_random_numbers(draw_weight, jurors_unconsumed_stake)?; let selections = Self::get_selections(&mut jurors, random_set); >::put(jurors); @@ -1503,20 +1509,13 @@ mod pallet { pub(crate) fn select_jurors( appeal_number: usize, ) -> Result, DispatchError> { - let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); - let random_jurors = Self::choose_multiple_weighted(necessary_jurors_weight)?; + let necessary_draws_weight = Self::necessary_draws_weight(appeal_number); + let random_jurors = Self::choose_multiple_weighted(necessary_draws_weight)?; - // keep in mind that the number of draws is at maximum necessary_jurors_weight * 2 + // keep in mind that the number of draws is at maximum necessary_draws_weight * 2 // because with delegations each juror draw weight // could delegate an additional juror in addition to the delegator itself - debug_assert!(random_jurors.len() <= 2 * necessary_jurors_weight as usize); - // ensure that we don't truncate some of the selections - debug_assert!( - random_jurors.len() <= T::MaxSelectedDraws::get() as usize, - "The number of randomly selected jurors should be less than or equal to \ - `MaxSelectedDraws`." - ); - + debug_assert!(random_jurors.len() <= 2 * necessary_draws_weight as usize); debug_assert!({ // proove that random jurors is sorted by juror account id // this is helpful to use binary search later on @@ -1527,11 +1526,18 @@ mod pallet { }); // what is the maximum number of draws with delegations? - // It is using necessary_jurors_weight (the number of atoms / draw weight) + // It is using necessary_draws_weight (the number of atoms / draw weight) // for the last round times two because each delegator // could potentially add one juror account to the selections // new appeal round should have a fresh set of draws + + // ensure that we don't truncate some of the selections + debug_assert!( + random_jurors.len() <= T::MaxSelectedDraws::get() as usize, + "The number of randomly selected jurors should be less than or equal to \ + `MaxSelectedDraws`." + ); Ok(>::truncate_from(random_jurors)) } @@ -1595,11 +1601,11 @@ mod pallet { court: &CourtOf, vote_item: &VoteItem, ) -> Result<(), DispatchError> { - match court.court_vote_item { - CourtVoteItem::Outcome => { + match court.vote_item_type { + VoteItemType::Outcome => { ensure!(vote_item.is_outcome(), Error::::InvalidVoteItemForOutcomeCourt); } - CourtVoteItem::Binary => { + VoteItemType::Binary => { ensure!(vote_item.is_binary(), Error::::InvalidVoteItemForBinaryCourt); } }; @@ -1634,7 +1640,7 @@ mod pallet { } // Calculates the necessary number of draws depending on the number of market appeals. - pub fn necessary_jurors_weight(appeals_len: usize) -> usize { + pub fn necessary_draws_weight(appeals_len: usize) -> usize { // 2^(appeals_len) * 31 + 2^(appeals_len) - 1 // MaxAppeals - 1 (= 3) example: 2^3 * 31 + 2^3 - 1 = 255 APPEAL_BASIS @@ -1916,9 +1922,9 @@ mod pallet { appeal_period: T::CourtAppealPeriod::get(), }; - let court_vote_item = CourtVoteItem::Outcome; + let vote_item_type = VoteItemType::Outcome; // sets cycle_ends one after the other from now - let court = CourtInfo::new(round_timing, court_vote_item); + let court = CourtInfo::new(round_timing, vote_item_type); let ids_len = T::DisputeResolution::add_auto_resolve(market_id, court.cycle_ends.appeal)?; @@ -2039,15 +2045,19 @@ mod pallet { let court_id = >::get(market_id) .ok_or(Error::::MarketIdToCourtIdNotFound)?; - let jurors_len: usize = JurorPool::::decode_len().unwrap_or(0); + let jurors = JurorPool::::get(); + let jurors_unconsumed_stake = Self::get_unconsumed_stake_sum(jurors.as_slice()); match >::get(court_id) { Some(court) => { let appeals = &court.appeals; let appeal_number = appeals.len().saturating_add(1); - let necessary_jurors_weight = Self::necessary_jurors_weight(appeal_number); + let necessary_draws_weight = Self::necessary_draws_weight(appeal_number); + let required_stake = (necessary_draws_weight as u128) + .saturating_mul(T::MinJurorStake::get().saturated_into::()); let valid_period = Self::check_appealable_market(court_id, &court, now).is_ok(); - if appeals.is_full() || (valid_period && (jurors_len < necessary_jurors_weight)) + if appeals.is_full() + || (valid_period && (jurors_unconsumed_stake < required_stake)) { has_failed = true; } @@ -2060,8 +2070,10 @@ mod pallet { let during_dispute_duration = report_block <= now && now < block_after_dispute_duration; - let necessary_jurors_weight = Self::necessary_jurors_weight(0usize); - if during_dispute_duration && jurors_len < necessary_jurors_weight { + let necessary_draws_weight = Self::necessary_draws_weight(0usize); + let required_stake = (necessary_draws_weight as u128) + .saturating_mul(T::MinJurorStake::get().saturated_into::()); + if during_dispute_duration && jurors_unconsumed_stake < required_stake { has_failed = true; } } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index c86a4196b..182aff8ab 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -635,7 +635,7 @@ fn vote_works() { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(court_id); - assert_eq!(draws.len(), Court::necessary_jurors_weight(0usize)); + assert_eq!(draws.len(), Court::necessary_draws_weight(0usize)); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); @@ -821,7 +821,7 @@ fn reveal_vote_works() { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(court_id); - assert_eq!(draws.len(), Court::necessary_jurors_weight(0usize)); + assert_eq!(draws.len(), Court::necessary_draws_weight(0usize)); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); @@ -1390,13 +1390,13 @@ fn appeal_draws_total_weight_is_correct() { let last_draws = >::get(court_id); let last_draws_total_weight = last_draws.iter().map(|draw| draw.weight).sum::(); - assert_eq!(last_draws_total_weight, Court::necessary_jurors_weight(0usize) as u32); + assert_eq!(last_draws_total_weight, Court::necessary_draws_weight(0usize) as u32); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let neccessary_juror_weight = Court::necessary_jurors_weight(1usize) as u32; + let neccessary_juror_weight = Court::necessary_draws_weight(1usize) as u32; let draws = >::get(court_id); let draws_total_weight = draws.iter().map(|draw| draw.weight).sum::(); assert_eq!(draws_total_weight, neccessary_juror_weight); @@ -2458,17 +2458,17 @@ fn on_global_dispute_returns_appealed_outcomes() { #[test] fn choose_multiple_weighted_works() { ExtBuilder::default().build().execute_with(|| { - let necessary_jurors_weight = Court::necessary_jurors_weight(0usize); - for i in 0..necessary_jurors_weight { + let necessary_draws_weight = Court::necessary_draws_weight(0usize); + for i in 0..necessary_draws_weight { let amount = MinJurorStake::get() + i as u128; let juror = i as u128; let _ = Balances::deposit(&juror, amount).unwrap(); assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - let random_jurors = Court::choose_multiple_weighted(necessary_jurors_weight).unwrap(); + let random_jurors = Court::choose_multiple_weighted(necessary_draws_weight).unwrap(); assert_eq!( random_jurors.iter().map(|draw| draw.weight).sum::() as usize, - necessary_jurors_weight + necessary_draws_weight ); }); } @@ -2501,15 +2501,15 @@ fn select_jurors_updates_juror_consumed_stake() { #[test_case(3usize; "fourth")] fn select_jurors_fails_if_not_enough_jurors(appeal_number: usize) { ExtBuilder::default().build().execute_with(|| { - let necessary_jurors_weight = Court::necessary_jurors_weight(appeal_number); - for i in 0..(necessary_jurors_weight - 1usize) { + let necessary_draws_weight = Court::necessary_draws_weight(appeal_number); + for i in 0..(necessary_draws_weight - 1usize) { let amount = MinJurorStake::get() + i as u128; let juror = (i + 1000) as u128; let _ = Balances::deposit(&juror, amount).unwrap(); assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - assert_noop!(Court::select_jurors(appeal_number), Error::::NotEnoughJurors); + assert_noop!(Court::select_jurors(appeal_number), Error::::NotEnoughJurorsStake); }); } @@ -2660,12 +2660,12 @@ fn has_failed_returns_true_for_uninitialized_court() { } #[test] -fn check_necessary_jurors_weight() { +fn check_necessary_draws_weight() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(Court::necessary_jurors_weight(0usize), 31usize); - assert_eq!(Court::necessary_jurors_weight(1usize), 63usize); - assert_eq!(Court::necessary_jurors_weight(2usize), 127usize); - assert_eq!(Court::necessary_jurors_weight(3usize), 255usize); + assert_eq!(Court::necessary_draws_weight(0usize), 31usize); + assert_eq!(Court::necessary_draws_weight(1usize), 63usize); + assert_eq!(Court::necessary_draws_weight(2usize), 127usize); + assert_eq!(Court::necessary_draws_weight(3usize), 255usize); }); } diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index bae3f1d12..1ad3a6750 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -32,7 +32,7 @@ pub type CourtId = u128; PartialEq, Eq, )] -pub enum CourtVoteItem { +pub enum VoteItemType { Outcome, Binary, } @@ -218,7 +218,7 @@ pub struct CourtInfo { pub appeals: Appeals, /// The information about the lifecycle of this court case. pub cycle_ends: CycleEnds, - pub court_vote_item: CourtVoteItem, + pub vote_item_type: VoteItemType, } /// The timing information about a court case. @@ -236,14 +236,14 @@ pub struct RoundTiming { impl CourtInfo { - pub fn new(round_timing: RoundTiming, court_vote_item: CourtVoteItem) -> Self { + pub fn new(round_timing: RoundTiming, vote_item_type: VoteItemType) -> Self { let pre_vote = round_timing.pre_vote_end; let vote = pre_vote.saturating_add(round_timing.vote_period); let aggregation = vote.saturating_add(round_timing.aggregation_period); let appeal = aggregation.saturating_add(round_timing.appeal_period); let cycle_ends = CycleEnds { pre_vote, vote, aggregation, appeal }; let status = CourtStatus::Open; - Self { status, appeals: Default::default(), cycle_ends, court_vote_item } + Self { status, appeals: Default::default(), cycle_ends, vote_item_type } } pub fn update_lifecycle(&mut self, round_timing: RoundTiming) { diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index d0e8789e0..54919c5f3 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-26, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-28, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -74,9 +74,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn join_court(j: u32) -> Weight { - Weight::from_ref_time(45_966_000) + Weight::from_ref_time(35_400_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(83_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(103_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -84,11 +84,11 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:6 w:1) // Storage: Balances Locks (r:1 w:1) fn delegate(j: u32, d: u32) -> Weight { - Weight::from_ref_time(41_432_000) + Weight::from_ref_time(34_177_000) // Standard Error: 0 .saturating_add(Weight::from_ref_time(134_000).saturating_mul(j.into())) - // Standard Error: 92_000 - .saturating_add(Weight::from_ref_time(1_358_000).saturating_mul(d.into())) + // Standard Error: 82_000 + .saturating_add(Weight::from_ref_time(2_902_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -96,32 +96,32 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) fn prepare_exit_court(j: u32) -> Weight { - Weight::from_ref_time(19_093_000) + Weight::from_ref_time(16_132_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(86_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(97_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn exit_court_remove() -> Weight { - Weight::from_ref_time(39_000_000) + Weight::from_ref_time(43_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn exit_court_set() -> Weight { - Weight::from_ref_time(37_000_000) + Weight::from_ref_time(41_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) fn vote(d: u32) -> Weight { - Weight::from_ref_time(50_448_000) + Weight::from_ref_time(66_992_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(99_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(51_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -131,9 +131,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) fn denounce_vote(d: u32) -> Weight { - Weight::from_ref_time(42_693_000) + Weight::from_ref_time(40_828_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(131_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(132_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -142,8 +142,10 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:0) // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) - fn reveal_vote(_d: u32) -> Weight { - Weight::from_ref_time(80_215_000) + fn reveal_vote(d: u32) -> Weight { + Weight::from_ref_time(76_131_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(85_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -154,20 +156,18 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:42 w:41) + // Storage: Court Jurors (r:201 w:198) // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Balances Reserves (r:1 w:1) fn appeal(j: u32, a: u32, _r: u32, _f: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(1_377_000).saturating_mul(j.into())) - // Standard Error: 689_000 - .saturating_add(Weight::from_ref_time(638_909_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(14)) - .saturating_add(T::DbWeight::get().reads((28_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(8)) - .saturating_add(T::DbWeight::get().writes((28_u64).saturating_mul(a.into()))) + // Standard Error: 36_000 + .saturating_add(Weight::from_ref_time(7_314_000).saturating_mul(j.into())) + // Standard Error: 10_623_000 + .saturating_add(Weight::from_ref_time(3_108_529_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads((119_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes((119_u64).saturating_mul(a.into()))) } // Storage: Court Courts (r:1 w:1) // Storage: Court SelectedDraws (r:1 w:1) @@ -175,8 +175,8 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: System Account (r:6 w:5) fn reassign_juror_stakes(d: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 13_000 - .saturating_add(Weight::from_ref_time(40_759_000).saturating_mul(d.into())) + // Standard Error: 18_000 + .saturating_add(Weight::from_ref_time(45_034_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -192,7 +192,7 @@ impl WeightInfoZeitgeist for WeightInfo { fn handle_inflation(j: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(13_078_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(13_129_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) @@ -200,35 +200,33 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:125 w:120) + // Storage: Court Jurors (r:35 w:30) fn select_jurors(a: u32) -> Weight { - Weight::from_ref_time(2_518_341_000) - // Standard Error: 34_748_000 - .saturating_add(Weight::from_ref_time(7_367_178_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(103)) - .saturating_add(T::DbWeight::get().reads((160_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(97)) - .saturating_add(T::DbWeight::get().writes((161_u64).saturating_mul(a.into()))) + Weight::from_ref_time(674_937_000) + // Standard Error: 9_406_000 + .saturating_add(Weight::from_ref_time(2_004_722_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(26)) + .saturating_add(T::DbWeight::get().reads((56_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(20)) + .saturating_add(T::DbWeight::get().writes((56_u64).saturating_mul(a.into()))) } // Storage: Court NextCourtId (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:4 w:4) + // Storage: Court Jurors (r:19 w:19) // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) // Storage: Court SelectedDraws (r:0 w:1) // Storage: Court CourtIdToMarketId (r:0 w:1) // Storage: Court MarketIdToCourtId (r:0 w:1) // Storage: Court Courts (r:0 w:1) - fn on_dispute(j: u32, r: u32) -> Weight { - Weight::from_ref_time(55_203_000) + fn on_dispute(j: u32, _r: u32) -> Weight { + Weight::from_ref_time(185_293_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(148_000).saturating_mul(j.into())) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(18_000).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(11)) - .saturating_add(T::DbWeight::get().writes(13)) + .saturating_add(Weight::from_ref_time(376_000).saturating_mul(j.into())) + .saturating_add(T::DbWeight::get().reads(30)) + .saturating_add(T::DbWeight::get().writes(32)) } // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:1) @@ -237,9 +235,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court Jurors (r:1 w:1) fn on_resolution(d: u32) -> Weight { - Weight::from_ref_time(19_040_000) - // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(4_329_000).saturating_mul(d.into())) + Weight::from_ref_time(13_229_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(4_108_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(1)) @@ -250,9 +248,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Balances Reserves (r:1 w:1) // Storage: System Account (r:1 w:1) fn exchange(a: u32) -> Weight { - Weight::from_ref_time(18_796_000) - // Standard Error: 50_000 - .saturating_add(Weight::from_ref_time(20_739_000).saturating_mul(a.into())) + Weight::from_ref_time(16_280_000) + // Standard Error: 29_000 + .saturating_add(Weight::from_ref_time(22_041_000).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(a.into()))) @@ -260,7 +258,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:0) fn get_auto_resolve() -> Weight { - Weight::from_ref_time(8_000_000).saturating_add(T::DbWeight::get().reads(2)) + Weight::from_ref_time(9_000_000).saturating_add(T::DbWeight::get().reads(2)) } // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court JurorPool (r:1 w:0) @@ -273,13 +271,13 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:1) // Storage: Court SelectedDraws (r:1 w:1) - // Storage: Court Jurors (r:94 w:94) + // Storage: Court Jurors (r:510 w:510) fn on_global_dispute(a: u32, d: u32) -> Weight { - Weight::from_ref_time(11_100_000) - // Standard Error: 18_000 - .saturating_add(Weight::from_ref_time(766_000).saturating_mul(a.into())) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(4_017_000).saturating_mul(d.into())) + Weight::from_ref_time(0) + // Standard Error: 518_000 + .saturating_add(Weight::from_ref_time(6_098_000).saturating_mul(a.into())) + // Standard Error: 4_000 + .saturating_add(Weight::from_ref_time(4_261_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -290,9 +288,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Court Courts (r:0 w:1) fn clear(d: u32) -> Weight { - Weight::from_ref_time(12_162_000) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(3_963_000).saturating_mul(d.into())) + Weight::from_ref_time(0) + // Standard Error: 9_000 + .saturating_add(Weight::from_ref_time(4_845_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index d53dc74b6..f192e5abb 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3126,7 +3126,7 @@ fn it_resolves_a_disputed_court_market() { } // just to have enough jurors for the dispute - for j in 1006..(1006 + Court::necessary_jurors_weight(0usize) as u32) { + for j in 1006..(1006 + Court::necessary_draws_weight(0usize) as u32) { let juror = j as u128; let amount = MinJurorStake::get() + juror; assert_ok!(AssetManager::deposit(Asset::Ztg, &juror, amount + SENTINEL_AMOUNT)); From ae99d7fac32fc8aecab7fc51fb5d84098453d9f6 Mon Sep 17 00:00:00 2001 From: Harald Heckmann Date: Wed, 3 May 2023 12:13:39 +0200 Subject: [PATCH 279/368] Use accurate dispute weight instead of max --- zrml/prediction-markets/src/lib.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 5ba8036a0..399f73b07 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -634,26 +634,26 @@ mod pallet { let market = >::market(&market_id)?; ensure!(market.status == MarketStatus::Reported, Error::::InvalidMarketStatus); - let overweight = match market.dispute_mechanism { + let weight = match market.dispute_mechanism { MarketDisputeMechanism::Authorized => { T::Authorized::on_dispute(&market_id, &market)?; - T::Court::on_dispute_max_weight() - .saturating_add(T::SimpleDisputes::on_dispute_max_weight()) + T::WeightInfo::dispute_authorized() } MarketDisputeMechanism::Court => { - T::Court::on_dispute(&market_id, &market)?; - T::Authorized::on_dispute_max_weight() - .saturating_add(T::SimpleDisputes::on_dispute_max_weight()) + let court_weight = T::Court::on_dispute(&market_id, &market)?.weight; + T::WeightInfo::dispute_authorized() + .saturating_sub(T::Authorized::on_dispute_max_weight()) + .saturating_add(court_weight) } MarketDisputeMechanism::SimpleDisputes => { - T::SimpleDisputes::on_dispute(&market_id, &market)?; - T::Court::on_dispute_max_weight() - .saturating_add(T::Authorized::on_dispute_max_weight()) + let sd_weight = T::SimpleDisputes::on_dispute(&market_id, &market)?.weight; + T::WeightInfo::dispute_authorized() + .saturating_sub(T::Authorized::on_dispute_max_weight()) + .saturating_add(sd_weight) } }; let dispute_bond = T::DisputeBond::get(); - T::AssetManager::reserve_named(&Self::reserve_id(), Asset::Ztg, &who, dispute_bond)?; >::mutate_market(&market_id, |m| { @@ -663,13 +663,7 @@ mod pallet { })?; Self::deposit_event(Event::MarketDisputed(market_id, MarketStatus::Disputed)); - - let full_weight = T::WeightInfo::dispute_authorized().saturating_add( - T::Court::on_dispute_max_weight() - .saturating_add(T::SimpleDisputes::on_dispute_max_weight()), - ); - - Ok((Some(full_weight.saturating_sub(overweight))).into()) + Ok((Some(weight)).into()) } /// Create a permissionless market, buy complete sets and deploy a pool with specified From 995eae36fc00abba361c2a7335b512a70a307d1f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 4 May 2023 12:24:04 +0200 Subject: [PATCH 280/368] take random ranges without replacement --- zrml/court/src/lib.rs | 225 +++++++++++++++++++++------------------- zrml/court/src/tests.rs | 23 ++-- 2 files changed, 136 insertions(+), 112 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b2e797481..2d693cb28 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -400,8 +400,8 @@ mod pallet { AmountExceedsBalance, /// After the first join of the court the amount has to be higher than the current stake. AmountBelowLastJoin, - /// The random number generation failed. - RandNumGenFailed, + /// The random number generation failed, because the juror total stake is too low. + NotEnoughTotalJurorStakeForRandomNumberGeneration, /// The amount is too low to kick the lowest juror out of the stake-weighted pool. AmountBelowLowestJuror, /// This should not happen, because the juror account should only be once in a pool. @@ -1119,28 +1119,62 @@ mod pallet { } // Get `n` unique and ordered random numbers from the random number generator. - // If the generator returns three times the same number in a row, an error is returned. + // This follows the rule of draw without replacement. pub(crate) fn get_n_random_numbers( n: usize, max: u128, ) -> Result, DispatchError> { let mut rng = Self::rng(); + let min_juror_stake = T::MinJurorStake::get().saturated_into::(); + + if max.checked_div(min_juror_stake).unwrap_or(0) < (n as u128) { + return Err(Error::::NotEnoughTotalJurorStakeForRandomNumberGeneration.into()); + } + let mut random_set = BTreeSet::new(); - let mut insert_unused_random_number = || -> DispatchResult { - let mut count = 0u8; - // this loop is to make sure we don't insert the same random number twice - while !random_set.insert(rng.gen_range(0u128..=max)) { - count = count.saturating_add(1u8); - if count >= 3u8 { - return Err(Error::::RandNumGenFailed.into()); - } + let mut remaining_ranges = Vec::new(); + remaining_ranges.push((0u128, max)); + + let mut insert_unused_random_number = || { + // create a cumulative vector of range sizes + let mut cumulative_ranges = Vec::new(); + let mut running_total = 0u128; + for &(start, end) in remaining_ranges.iter() { + let range_size = end.saturating_sub(start); + running_total = running_total.saturating_add(range_size); + cumulative_ranges.push(running_total); + } + + // generate a random number within the total range size + let random_number = rng.gen_range(0..=running_total); + + // find the corresponding range using binary search + let range_index = + cumulative_ranges.binary_search(&random_number).unwrap_or_else(|x| x); + + // choose a random range according to stake-weight from the remaining_ranges vector + let (start, end) = remaining_ranges[range_index]; + + // generate a random number within the chosen range + let random_number = rng.gen_range(start..=end); + random_set.insert(random_number); + + // update the remaining_ranges vector by splitting the chosen range + let left_range = (start, random_number); + let right_range = (random_number.saturating_add(min_juror_stake), end); + + remaining_ranges.remove(range_index); + if left_range.0 <= left_range.1 { + remaining_ranges.push(left_range); + } + if right_range.0 <= right_range.1 { + remaining_ranges.push(right_range); } - Ok(()) }; for _ in 0..n { - insert_unused_random_number()?; + insert_unused_random_number(); } debug_assert!(random_set.len() == n); @@ -1148,53 +1182,15 @@ mod pallet { Ok(random_set) } - // Get the sum of the unconsumed stake from all jurors in the pool. - // The unconsumed stake is the stake that was not already locked in previous courts. - fn get_unconsumed_stake_sum(jurors: &[JurorPoolItemOf]) -> u128 { - jurors - .iter() - .map(|pool_item| { - pool_item - .stake - .saturating_sub(pool_item.consumed_stake) - .saturated_into::() - }) - .sum::() - } - - // Returns the added active lock amount. + // Adds active lock amount. // The added active lock amount is noted in the Jurors map. - fn update_active_lock( - juror: &T::AccountId, - selections: &BTreeMap>, - ) -> BalanceOf { - if let Some(SelectionValue { - weight: _, - slashable: total_lock_added, - delegated_stakes: _, - }) = selections.get(juror) - { - if let Some(mut juror_info) = >::get(juror) { - juror_info.active_lock = - juror_info.active_lock.saturating_add(*total_lock_added); - >::insert(juror, juror_info); - } else { - debug_assert!(false, "Juror should exist in the Jurors map"); - } - return *total_lock_added; - } - - >::zero() - } - - // Returns true, if `n` is greater or equal to `lower_bound` and less than `upper_bound`. - // Returns false, otherwise. - fn in_range(n: u128, lower_bound: u128, upper_bound: u128) -> bool { - debug_assert!(lower_bound <= upper_bound); - if lower_bound <= n && n < upper_bound { - return true; + fn add_active_lock(juror: &T::AccountId, lock_added: BalanceOf) { + if let Some(mut juror_info) = >::get(juror) { + juror_info.active_lock = juror_info.active_lock.saturating_add(lock_added); + >::insert(juror, juror_info); + } else { + debug_assert!(false, "Juror should exist in the Jurors map"); } - false } /// Add a delegated juror to the `delegated_stakes` vector. @@ -1369,57 +1365,54 @@ mod pallet { // is increased by the random selection weight. fn get_selections( jurors: &mut JurorPoolOf, - mut random_set: BTreeSet, + random_set: BTreeSet, + cumulative_ranges: Vec, ) -> BTreeMap> { - let mut selections = BTreeMap::>::new(); + debug_assert!(jurors.len() == cumulative_ranges.len()); + debug_assert!({ + let prev = cumulative_ranges.clone(); + let mut sorted = cumulative_ranges.clone(); + sorted.sort(); + prev.len() == sorted.len() && prev.iter().zip(sorted.iter()).all(|(a, b)| a == b) + }); + debug_assert!({ + random_set.iter().all(|random_number| { + let last = cumulative_ranges.last().unwrap_or(&0); + *random_number <= *last + }) + }); + let mut selections = BTreeMap::>::new(); let mut invalid_juror_indices = Vec::::new(); - let mut current_weight = 0u128; - for (i, pool_item) in jurors.iter_mut().enumerate() { - let lower_bound = current_weight; - let mut unconsumed = pool_item.stake.saturating_sub(pool_item.consumed_stake); - let upper_bound = - current_weight.saturating_add(unconsumed.saturated_into::()); - - // this always gets the lowest random number first and maybe removes it - let unchangable_random_numbers = random_set.clone(); - for random_number in unchangable_random_numbers.iter() { - if Self::in_range(*random_number, lower_bound, upper_bound) { - let lock_added = unconsumed.min(T::MinJurorStake::get()); - unconsumed = unconsumed.saturating_sub(lock_added); - - match Self::add_to_selections( - &mut selections, - &pool_item.juror, - lock_added, - *random_number, - ) { - Ok(()) => {} - Err(SelectionError::NoValidDelegatedJuror) => { - // it would be pretty expensive to request another selection - // so just ignore this missing MinJurorStake - // I mean we also miss MinJurorStake in the case - // if the juror fails to vote or reveal or gets denounced - invalid_juror_indices.push(i); - } + for random_number in random_set { + let range_index = + cumulative_ranges.binary_search(&random_number).unwrap_or_else(|i| i); + if let Some(pool_item) = jurors.get_mut(range_index) { + let unconsumed = pool_item.stake.saturating_sub(pool_item.consumed_stake); + let lock_added = unconsumed.min(T::MinJurorStake::get()); + + match Self::add_to_selections( + &mut selections, + &pool_item.juror, + lock_added, + random_number, + ) { + Ok(()) => {} + Err(SelectionError::NoValidDelegatedJuror) => { + // it would be pretty expensive to request another selection + // so just ignore this missing MinJurorStake + // I mean we also miss MinJurorStake in the case + // if the juror fails to vote or reveal or gets denounced + invalid_juror_indices.push(range_index); } - - random_set.remove(random_number); - } else { - break; } - } - let total_lock_added = Self::update_active_lock(&pool_item.juror, &selections); - pool_item.consumed_stake = - pool_item.consumed_stake.saturating_add(total_lock_added); - - if random_set.is_empty() { - break; + Self::add_active_lock(&pool_item.juror, lock_added); + pool_item.consumed_stake = pool_item.consumed_stake.saturating_add(lock_added); + } else { + debug_assert!(false, "Each range index should match to a juror."); } - - current_weight = upper_bound; } for i in invalid_juror_indices { @@ -1467,12 +1460,25 @@ mod pallet { draw_weight: usize, ) -> Result>, DispatchError> { let mut jurors = >::get(); - let jurors_unconsumed_stake = Self::get_unconsumed_stake_sum(jurors.as_slice()); + + let mut total_unconsumed = 0u128; + let mut cumulative_ranges = Vec::new(); + let mut running_total = 0u128; + for pool_item in &jurors { + let unconsumed = pool_item + .stake + .saturating_sub(pool_item.consumed_stake) + .saturated_into::(); + total_unconsumed = total_unconsumed.saturating_add(unconsumed); + running_total = running_total.saturating_add(unconsumed); + cumulative_ranges.push(running_total); + } + let required_stake = (draw_weight as u128) .saturating_mul(T::MinJurorStake::get().saturated_into::()); - ensure!(jurors_unconsumed_stake >= required_stake, Error::::NotEnoughJurorsStake); - let random_set = Self::get_n_random_numbers(draw_weight, jurors_unconsumed_stake)?; - let selections = Self::get_selections(&mut jurors, random_set); + ensure!(total_unconsumed >= required_stake, Error::::NotEnoughJurorsStake); + let random_set = Self::get_n_random_numbers(draw_weight, total_unconsumed)?; + let selections = Self::get_selections(&mut jurors, random_set, cumulative_ranges); >::put(jurors); Ok(Self::convert_selections_to_draws(selections)) @@ -2046,7 +2052,14 @@ mod pallet { .ok_or(Error::::MarketIdToCourtIdNotFound)?; let jurors = JurorPool::::get(); - let jurors_unconsumed_stake = Self::get_unconsumed_stake_sum(jurors.as_slice()); + let jurors_unconsumed_stake = jurors.iter().fold(0u128, |acc, pool_item| { + let unconsumed = pool_item + .stake + .saturating_sub(pool_item.consumed_stake) + .saturated_into::(); + acc.saturating_add(unconsumed) + }); + match >::get(court_id) { Some(court) => { let appeals = &court.appeals; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 182aff8ab..c1d8db3d3 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -635,7 +635,10 @@ fn vote_works() { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(court_id); - assert_eq!(draws.len(), Court::necessary_draws_weight(0usize)); + assert_eq!( + draws.iter().map(|draw| draw.weight).sum::() as usize, + Court::necessary_draws_weight(0usize) + ); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); @@ -821,7 +824,10 @@ fn reveal_vote_works() { // trick a little bit to let alice be part of the ("random") selection let mut draws = >::get(court_id); - assert_eq!(draws.len(), Court::necessary_draws_weight(0usize)); + assert_eq!( + draws.iter().map(|draw| draw.weight).sum::() as usize, + Court::necessary_draws_weight(0usize) + ); let slashable = MinJurorStake::get(); let alice_index = draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); @@ -2583,18 +2589,23 @@ fn on_dispute_inserts_draws() { draws[2], Draw { juror: CHARLIE, - weight: 8, + weight: 7, vote: Vote::Drawn, - slashable: 8 * MinJurorStake::get() + slashable: 7 * MinJurorStake::get() } ); assert_eq!( draws[3], - Draw { juror: DAVE, weight: 8, vote: Vote::Drawn, slashable: 8 * MinJurorStake::get() } + Draw { juror: DAVE, weight: 7, vote: Vote::Drawn, slashable: 7 * MinJurorStake::get() } ); assert_eq!( draws[4], - Draw { juror: EVE, weight: 8, vote: Vote::Drawn, slashable: 8 * MinJurorStake::get() } + Draw { + juror: EVE, + weight: 10, + vote: Vote::Drawn, + slashable: 10 * MinJurorStake::get() + } ); assert_eq!(draws.len(), 5usize); }); From 9c814acc0cb08d5b3d18c4f1b20304ec52b71df4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 5 May 2023 09:50:15 +0200 Subject: [PATCH 281/368] add start_global_dispute tests --- zrml/global-disputes/src/tests.rs | 96 ++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index ce5cabfc3..26bb98fb8 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -30,7 +30,7 @@ use frame_support::{ BoundedVec, }; use pallet_balances::{BalanceLock, Error as BalancesError}; -use sp_runtime::traits::Zero; +use sp_runtime::{traits::Zero, SaturatedConversion}; use test_case::test_case; use zeitgeist_primitives::{ constants::mock::{ @@ -283,6 +283,100 @@ fn start_global_dispute_fails_if_max_owner_reached() { }); } +#[test] +fn start_global_dispute_fails_if_shared_possession_required() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let outcome_info = OutcomeInfo { + outcome_sum: SETUP_AMOUNT, + possession: Possession::Paid { owner: ALICE, fee: VotingOutcomeFee::get() }, + }; + >::insert(market_id, OutcomeReport::Scalar(0), outcome_info); + + let mut initial_items = Vec::new(); + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(0), + owner: 0u128, + amount: SETUP_AMOUNT, + }); + for i in 0..MaxOwners::get() + 1 { + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(42), + owner: i.into(), + amount: SETUP_AMOUNT, + }); + } + + assert_eq!( + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), + Err(Error::::SharedPossessionRequired.into()) + ); + }); +} + +#[test] +fn start_global_dispute_adds_owners_for_existing_outcome() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let mut initial_items = Vec::new(); + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(0), + owner: 0u128, + amount: SETUP_AMOUNT, + }); + for i in 0..MaxOwners::get() { + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(42), + owner: i.into(), + amount: SETUP_AMOUNT, + }); + } + + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); + + let outcome_info = >::get(market_id, OutcomeReport::Scalar(42)).unwrap(); + assert_eq!( + outcome_info.possession, + Possession::Shared { owners: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9].try_into().unwrap() } + ); + }); +} + +#[test] +fn start_global_dispute_updates_to_highest_winner() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + let mut initial_items = Vec::new(); + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(0), + owner: 0u128, + amount: SETUP_AMOUNT, + }); + for i in 0..MaxOwners::get() { + initial_items.push(InitialItem { + outcome: OutcomeReport::Scalar(42 + i.saturated_into::()), + owner: i.into(), + amount: SETUP_AMOUNT + i.saturated_into::(), + }); + } + + assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); + + let gd_info = >::get(market_id).unwrap(); + assert_eq!(gd_info.outcome_info.outcome_sum, SETUP_AMOUNT + 9); + assert_eq!(gd_info.winner_outcome, OutcomeReport::Scalar(51)); + }); +} + #[test] fn add_vote_outcome_fails_with_outcome_mismatch() { ExtBuilder::default().build().execute_with(|| { From bd28a0d94b6e407b881432ada88c9fc41aaeab75 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 5 May 2023 09:53:25 +0200 Subject: [PATCH 282/368] add test --- zrml/global-disputes/src/tests.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 26bb98fb8..29f1d13d1 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -239,6 +239,26 @@ fn start_global_dispute_fails_if_less_than_two_outcomes() { }); } +#[test] +fn start_global_dispute_fails_if_not_two_unique_outcomes() { + ExtBuilder::default().build().execute_with(|| { + let market_id = 0u128; + let market = market_mock::(); + Markets::::insert(market_id, &market); + + // three times the same outcome => not at least two unique outcomes + let initial_items = vec![ + InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(0), owner: BOB, amount: SETUP_AMOUNT }, + InitialItem { outcome: OutcomeReport::Scalar(0), owner: CHARLIE, amount: SETUP_AMOUNT }, + ]; + assert_eq!( + GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), + Err(Error::::AtLeastTwoUniqueOutcomesRequired.into()) + ); + }); +} + #[test] fn start_global_dispute_fails_if_already_exists() { ExtBuilder::default().build().execute_with(|| { From b4b040b6957511a3c89a691de5f536f95b5f97f0 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 5 May 2023 10:01:11 +0200 Subject: [PATCH 283/368] fix typo --- zrml/global-disputes/src/global_disputes_pallet_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/global-disputes/src/global_disputes_pallet_api.rs b/zrml/global-disputes/src/global_disputes_pallet_api.rs index 54c7e9af9..28dc58e5e 100644 --- a/zrml/global-disputes/src/global_disputes_pallet_api.rs +++ b/zrml/global-disputes/src/global_disputes_pallet_api.rs @@ -41,7 +41,7 @@ pub trait GlobalDisputesPalletApi { /// In case of a duplicated outcome, the owner and amount is added to the pre-existing outcome. fn start_global_dispute( market_id: &MarketId, - initial_item: &[InitialItem], + initial_items: &[InitialItem], ) -> Result; /// Determine the winner of a global dispute. From 1001f0800e65f7aef7601aa7b7b9f397eb56cb11 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 8 May 2023 12:22:22 +0200 Subject: [PATCH 284/368] switch get_random_number algo --- zrml/court/src/lib.rs | 70 +++++++++++++-------------------------- zrml/court/src/tests.rs | 17 ++++------ zrml/court/src/weights.rs | 16 ++++----- 3 files changed, 37 insertions(+), 66 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 2d693cb28..7b6e90516 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -52,7 +52,7 @@ use frame_system::{ ensure_signed, pallet_prelude::{BlockNumberFor, OriginFor}, }; -use rand::{Rng, RngCore, SeedableRng}; +use rand::{RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; use sp_arithmetic::{per_things::Perquintill, traits::One}; use sp_runtime::{ @@ -1120,61 +1120,37 @@ mod pallet { // Get `n` unique and ordered random numbers from the random number generator. // This follows the rule of draw without replacement. - pub(crate) fn get_n_random_numbers( + pub(crate) fn get_n_random_section_starts( n: usize, max: u128, ) -> Result, DispatchError> { let mut rng = Self::rng(); let min_juror_stake = T::MinJurorStake::get().saturated_into::(); - - if max.checked_div(min_juror_stake).unwrap_or(0) < (n as u128) { + let mut sections_len = max.checked_div(min_juror_stake).unwrap_or(0); + let last_partial_section_exists = max % min_juror_stake != 0; + if last_partial_section_exists { + // the last partial min_juror_stake counts as a full min_juror_stake + sections_len = sections_len.saturating_add(1); + } + if sections_len < (n as u128) { return Err(Error::::NotEnoughTotalJurorStakeForRandomNumberGeneration.into()); } let mut random_set = BTreeSet::new(); - let mut remaining_ranges = Vec::new(); - remaining_ranges.push((0u128, max)); - - let mut insert_unused_random_number = || { - // create a cumulative vector of range sizes - let mut cumulative_ranges = Vec::new(); - let mut running_total = 0u128; - for &(start, end) in remaining_ranges.iter() { - let range_size = end.saturating_sub(start); - running_total = running_total.saturating_add(range_size); - cumulative_ranges.push(running_total); - } - - // generate a random number within the total range size - let random_number = rng.gen_range(0..=running_total); - - // find the corresponding range using binary search - let range_index = - cumulative_ranges.binary_search(&random_number).unwrap_or_else(|x| x); - - // choose a random range according to stake-weight from the remaining_ranges vector - let (start, end) = remaining_ranges[range_index]; - - // generate a random number within the chosen range - let random_number = rng.gen_range(start..=end); - random_set.insert(random_number); - - // update the remaining_ranges vector by splitting the chosen range - let left_range = (start, random_number); - let right_range = (random_number.saturating_add(min_juror_stake), end); - - remaining_ranges.remove(range_index); - if left_range.0 <= left_range.1 { - remaining_ranges.push(left_range); - } - if right_range.0 <= right_range.1 { - remaining_ranges.push(right_range); - } - }; - - for _ in 0..n { - insert_unused_random_number(); + let indices: Vec = (0..sections_len).collect(); + let last_index = sections_len.saturating_sub(1); + use rand::seq::SliceRandom; + // Pick `n` unique random indices without repitition (replacement) + for &index in indices.as_slice().choose_multiple(&mut rng, n) { + let is_last_index = index == last_index; + let random_section_start = if last_partial_section_exists && is_last_index { + // max is the last possible section start + max + } else { + index.saturating_mul(min_juror_stake) + }; + random_set.insert(random_section_start); } debug_assert!(random_set.len() == n); @@ -1477,7 +1453,7 @@ mod pallet { let required_stake = (draw_weight as u128) .saturating_mul(T::MinJurorStake::get().saturated_into::()); ensure!(total_unconsumed >= required_stake, Error::::NotEnoughJurorsStake); - let random_set = Self::get_n_random_numbers(draw_weight, total_unconsumed)?; + let random_set = Self::get_n_random_section_starts(draw_weight, total_unconsumed)?; let selections = Self::get_selections(&mut jurors, random_set, cumulative_ranges); >::put(jurors); diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index c1d8db3d3..ba1d2b8bb 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -2576,22 +2576,22 @@ fn on_dispute_inserts_draws() { draws[0], Draw { juror: ALICE, - weight: 3, + weight: 4, vote: Vote::Drawn, - slashable: 3 * MinJurorStake::get() + slashable: 4 * MinJurorStake::get() } ); assert_eq!( draws[1], - Draw { juror: BOB, weight: 4, vote: Vote::Drawn, slashable: 4 * MinJurorStake::get() } + Draw { juror: BOB, weight: 5, vote: Vote::Drawn, slashable: 5 * MinJurorStake::get() } ); assert_eq!( draws[2], Draw { juror: CHARLIE, - weight: 7, + weight: 6, vote: Vote::Drawn, - slashable: 7 * MinJurorStake::get() + slashable: 6 * MinJurorStake::get() } ); assert_eq!( @@ -2600,12 +2600,7 @@ fn on_dispute_inserts_draws() { ); assert_eq!( draws[4], - Draw { - juror: EVE, - weight: 10, - vote: Vote::Drawn, - slashable: 10 * MinJurorStake::get() - } + Draw { juror: EVE, weight: 9, vote: Vote::Drawn, slashable: 9 * MinJurorStake::get() } ); assert_eq!(draws.len(), 5usize); }); diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 54919c5f3..12bf05fbd 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -200,15 +200,15 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:35 w:30) + // Storage: Court Jurors (r:31 w:30) fn select_jurors(a: u32) -> Weight { - Weight::from_ref_time(674_937_000) - // Standard Error: 9_406_000 - .saturating_add(Weight::from_ref_time(2_004_722_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(26)) - .saturating_add(T::DbWeight::get().reads((56_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(20)) - .saturating_add(T::DbWeight::get().writes((56_u64).saturating_mul(a.into()))) + Weight::from_ref_time(531_137_000) + // Standard Error: 7_595_000 + .saturating_add(Weight::from_ref_time(1_598_167_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(20)) + .saturating_add(T::DbWeight::get().reads((61_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(18)) + .saturating_add(T::DbWeight::get().writes((61_u64).saturating_mul(a.into()))) } // Storage: Court NextCourtId (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) From 5ef62a8ca8fa9ce6d74d6749865cd74f17992064 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 8 May 2023 15:45:54 +0200 Subject: [PATCH 285/368] integrate court and reject simple disputes --- runtime/battery-station/src/lib.rs | 1 + zrml/prediction-markets/src/benchmarks.rs | 2 +- zrml/prediction-markets/src/lib.rs | 5 +- zrml/prediction-markets/src/tests.rs | 116 +++++----------------- 4 files changed, 26 insertions(+), 98 deletions(-) diff --git a/runtime/battery-station/src/lib.rs b/runtime/battery-station/src/lib.rs index f4debff3c..142afdf62 100644 --- a/runtime/battery-station/src/lib.rs +++ b/runtime/battery-station/src/lib.rs @@ -113,6 +113,7 @@ impl Contains for IsCallable { #[allow(clippy::match_like_matches_macro)] match call { + Call::SimpleDisputes(_) => false, Call::LiquidityMining(_) => false, Call::PredictionMarkets(inner_call) => { match inner_call { diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 47134cef8..20ce02128 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -800,7 +800,7 @@ benchmarks! { )?; >::mutate_market(&market_id, |market| { - market.dispute_mechanism = MarketDisputeMechanism::SimpleDisputes; + market.dispute_mechanism = MarketDisputeMechanism::Court; Ok(()) })?; diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 50293e2c6..4674b94fe 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1441,10 +1441,7 @@ mod pallet { ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); ensure!( - matches!( - market.dispute_mechanism, - MarketDisputeMechanism::Court | MarketDisputeMechanism::SimpleDisputes - ), + matches!(market.dispute_mechanism, MarketDisputeMechanism::Court), Error::::InvalidDisputeMechanism ); diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index f192e5abb..c6d48218e 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -50,6 +50,7 @@ use zeitgeist_primitives::{ Moment, MultiHash, OutcomeReport, PoolStatus, ScalarPosition, ScoringRule, }, }; +use zrml_global_disputes::GlobalDisputesPalletApi; use zrml_market_commons::MarketCommonsPalletApi; use zrml_swaps::Pools; @@ -3399,6 +3400,7 @@ fn it_appeals_a_court_market_to_global_dispute() { OutcomeReport::Categorical(0) )); + let dispute_block = report_at; assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); for _ in 0..(MaxAppeals::get() - 1) { @@ -3423,7 +3425,28 @@ fn it_appeals_a_court_market_to_global_dispute() { CError::::MaxAppealsReached ); + assert!(!GlobalDisputes::is_started(&market_id)); + assert_ok!(PredictionMarkets::start_global_dispute(Origin::signed(BOB), market_id)); + + let now = >::block_number(); + + assert!(GlobalDisputes::is_started(&market_id)); + System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); + + // remove_last_dispute_from_market_ids_per_dispute_block works + let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); + assert_eq!(removable_market_ids.len(), 0); + + let market_ids = MarketIdsPerDisputeBlock::::get( + now + ::GlobalDisputePeriod::get(), + ); + assert_eq!(market_ids, vec![market_id]); + + assert_noop!( + PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), + Error::::GlobalDisputeAlreadyStarted + ); }; ExtBuilder::default().build().execute_with(|| { test(Asset::Ztg); @@ -3462,99 +3485,6 @@ fn dispute_fails_unless_reported_or_disputed_market(status: MarketStatus) { }); } -#[test] -fn start_global_dispute_works() { - ExtBuilder::default().build().execute_with(|| { - let end = 2; - assert_ok!(PredictionMarkets::create_market( - Origin::signed(ALICE), - Asset::Ztg, - BOB, - MarketPeriod::Block(0..2), - get_deadlines(), - gen_metadata(2), - MarketCreation::Permissionless, - MarketType::Categorical(::MaxDisputes::get() + 1), - MarketDisputeMechanism::SimpleDisputes, - ScoringRule::CPMM, - )); - let market_id = MarketCommons::latest_market_id().unwrap(); - - let market = MarketCommons::market(&market_id).unwrap(); - let grace_period = market.deadlines.grace_period; - run_to_block(end + grace_period + 1); - assert_ok!(PredictionMarkets::report( - Origin::signed(BOB), - market_id, - OutcomeReport::Categorical(0) - )); - let dispute_at_0 = end + grace_period + 2; - run_to_block(dispute_at_0); - assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); - for i in 1..=::MaxDisputes::get() { - assert_noop!( - PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), - Error::::MarketDisputeMechanismNotFailed - ); - - assert_ok!(SimpleDisputes::suggest_outcome( - Origin::signed(CHARLIE), - market_id, - OutcomeReport::Categorical(i.saturated_into()), - )); - run_blocks(1); - let market = MarketCommons::market(&market_id).unwrap(); - assert_eq!(market.status, MarketStatus::Disputed); - } - - let disputes = zrml_simple_disputes::Disputes::::get(market_id); - assert_eq!(disputes.len(), ::MaxDisputes::get() as usize); - - let last_dispute = disputes.last().unwrap(); - let dispute_block = last_dispute.at.saturating_add(market.deadlines.dispute_duration); - let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); - assert_eq!(removable_market_ids.len(), 1); - - use zrml_global_disputes::GlobalDisputesPalletApi; - - let now = >::block_number(); - assert_ok!(PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id)); - - // report check - assert_eq!( - GlobalDisputes::get_voting_outcome_info(&market_id, &OutcomeReport::Categorical(0)), - Some((Zero::zero(), vec![BOB])), - ); - for i in 1..=::MaxDisputes::get() { - let dispute_bond = - zrml_simple_disputes::default_outcome_bond::((i - 1).into()); - assert_eq!( - GlobalDisputes::get_voting_outcome_info( - &market_id, - &OutcomeReport::Categorical(i.saturated_into()) - ), - Some((dispute_bond, vec![CHARLIE])), - ); - } - - // remove_last_dispute_from_market_ids_per_dispute_block works - let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); - assert_eq!(removable_market_ids.len(), 0); - - let market_ids = MarketIdsPerDisputeBlock::::get( - now + ::GlobalDisputePeriod::get(), - ); - assert_eq!(market_ids, vec![market_id]); - assert!(GlobalDisputes::is_started(&market_id)); - System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); - - assert_noop!( - PredictionMarkets::start_global_dispute(Origin::signed(CHARLIE), market_id), - Error::::GlobalDisputeAlreadyStarted - ); - }); -} - #[test] fn start_global_dispute_fails_on_wrong_mdm() { ExtBuilder::default().build().execute_with(|| { From 44d788abfc75f2a095cd6fa8b503d17b0a2be78f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 9 May 2023 09:35:41 +0200 Subject: [PATCH 286/368] update start_global_dispute bench --- zrml/prediction-markets/src/benchmarks.rs | 48 +++++++++++------------ zrml/prediction-markets/src/lib.rs | 6 +-- zrml/prediction-markets/src/weights.rs | 25 +++++++----- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 20ce02128..a47db04ea 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -224,7 +224,7 @@ fn setup_reported_categorical_market_with_pool::MarketCommons as MarketCommonsPalletApi>::MarketId: From<::MarketId>, } @@ -811,6 +811,23 @@ benchmarks! { market_ids_1.try_push(i.saturated_into()).unwrap(); } + >::on_initialize(1u32.into()); + >::set_block_number(1u32.into()); + + let min_amount = ::MinJurorStake::get(); + for i in 0..>::necessary_draws_weight(0usize) { + let juror: T::AccountId = account("Jurori", i.try_into().unwrap(), 0); + ::AssetManager::deposit( + Asset::Ztg, + &juror, + u128::MAX.saturated_into(), + ).unwrap(); + >::join_court( + RawOrigin::Signed(juror.clone()).into(), + min_amount + i.saturated_into(), + )?; + } + let disputor: T::AccountId = account("Disputor", 1, 0); ::AssetManager::deposit( Asset::Ztg, @@ -822,38 +839,21 @@ benchmarks! { } .dispatch_bypass_filter(RawOrigin::Signed(disputor).into())?; - let max_dispute_len = ::MaxDisputes::get(); - for i in 0..max_dispute_len { - // ensure that the MarketIdsPerDisputeBlock does not interfere - // with the start_global_dispute execution block - >::set_block_number(i.saturated_into()); - let reserver: T::AccountId = account("Reserver", i, 0); - ::AssetManager::deposit(Asset::Ztg, &reserver, (u128::MAX).saturated_into())?; - let market_id_number: u128 = market_id.saturated_into::(); - let _ = zrml_simple_disputes::Call::::suggest_outcome { - market_id: market_id_number.saturated_into(), - outcome: OutcomeReport::Scalar(i.saturated_into()), - }.dispatch_bypass_filter(RawOrigin::Signed(reserver.clone()).into())?; - } - let market = >::market(&market_id.saturated_into()).unwrap(); - let market_id_number: u128 = market_id.saturated_into::(); - let market_id_simple: zrml_simple_disputes::MarketIdOf = market_id_number.saturated_into(); - let disputes = zrml_simple_disputes::Disputes::::get(market_id_simple); - let last_dispute = disputes.last().unwrap(); - let dispute_duration_ends_at_block = last_dispute.at + market.deadlines.dispute_duration; + use zeitgeist_primitives::traits::DisputeApi; + let appeal_end = T::Court::get_auto_resolve(&market_id, &market).result.unwrap(); let mut market_ids_2: BoundedVec, CacheSize> = BoundedVec::try_from( vec![market_id], ).unwrap(); for i in 1..n { market_ids_2.try_push(i.saturated_into()).unwrap(); } - MarketIdsPerDisputeBlock::::insert(dispute_duration_ends_at_block, market_ids_2); + MarketIdsPerDisputeBlock::::insert(appeal_end, market_ids_2); - let current_block: T::BlockNumber = (max_dispute_len + 1).saturated_into(); - >::set_block_number(current_block); + >::set_block_number(appeal_end - 1u64.saturated_into::()); - let global_dispute_end = current_block + T::GlobalDisputePeriod::get(); + let now = >::block_number(); + let global_dispute_end = now + T::GlobalDisputePeriod::get(); // the complexity depends on MarketIdsPerDisputeBlock at the current block // this is because a variable number of market ids need to be decoded from the storage MarketIdsPerDisputeBlock::::insert(global_dispute_end, market_ids_1); diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 4674b94fe..cb393c60a 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -1418,17 +1418,15 @@ mod pallet { Ok(Some(T::WeightInfo::sell_complete_set(assets_len)).into()) } - /// When the `MaxDisputes` amount of disputes is reached, - /// this allows to start a global dispute. + /// Start a global dispute, if the market dispute mechanism fails. /// /// # Arguments /// /// * `market_id`: The identifier of the market. /// /// NOTE: - /// The outcomes of the disputes and the report outcome + /// The returned outcomes of the market dispute mechanism and the report outcome /// are added to the global dispute voting outcomes. - /// The bond of each dispute is the initial vote amount. #[pallet::weight(T::WeightInfo::start_global_dispute(CacheSize::get(), CacheSize::get()))] #[transactional] pub fn start_global_dispute( diff --git a/zrml/prediction-markets/src/weights.rs b/zrml/prediction-markets/src/weights.rs index b82f6f825..353a06196 100644 --- a/zrml/prediction-markets/src/weights.rs +++ b/zrml/prediction-markets/src/weights.rs @@ -282,18 +282,23 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(a.into()))) } // Storage: MarketCommons Markets (r:1 w:0) - // Storage: PredictionMarkets Disputes (r:1 w:0) // Storage: GlobalDisputes Winners (r:1 w:1) - // Storage: GlobalDisputes Outcomes (r:7 w:7) - // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) + // Storage: Court MarketIdToCourtId (r:1 w:0) + // Storage: Court JurorPool (r:1 w:0) + // Storage: Court Courts (r:1 w:1) + // Storage: Court CourtIdToMarketId (r:1 w:0) + // Storage: Court SelectedDraws (r:1 w:1) + // Storage: Court Jurors (r:30 w:30) + // Storage: GlobalDisputes Outcomes (r:1 w:1) + // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) fn start_global_dispute(m: u32, n: u32) -> Weight { - Weight::from_ref_time(128_368_000) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(8_000).saturating_mul(m.into())) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(96_000).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(10)) + Weight::from_ref_time(203_655_000) + // Standard Error: 1_000 + .saturating_add(Weight::from_ref_time(67_000).saturating_mul(m.into())) + // Standard Error: 1_000 + .saturating_add(Weight::from_ref_time(15_000).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(39)) + .saturating_add(T::DbWeight::get().writes(35)) } // Storage: PredictionMarkets Disputes (r:1 w:1) // Storage: MarketCommons Markets (r:1 w:1) From 6025960822411d6e2283131539e8e8e2fc9fbe48 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 9 May 2023 10:04:11 +0200 Subject: [PATCH 287/368] fix clippy --- zrml/prediction-markets/src/mock.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 1852fe593..4f0ef9f02 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -44,8 +44,8 @@ use zeitgeist_primitives::{ MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSubsidyPeriod, MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, - MinDisputeDuration, MinLiquidity, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, - MinWeight, MinimumPeriod, OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, + MinDisputeDuration, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, + MinimumPeriod, OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, SimpleDisputesPalletId, StakeWeight, SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, From 2e930872d8814ab92f8edc40e649c99cdb747a6f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 9 May 2023 16:03:56 +0200 Subject: [PATCH 288/368] use efficient draw without replacement algorithm --- zrml/court/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++------ zrml/court/src/tests.rs | 12 ++++++------ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7b6e90516..a38ff3b5e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1118,14 +1118,40 @@ mod pallet { Weight::zero() } - // Get `n` unique and ordered random numbers from the random number generator. + // The algorithm uses reservoir sampling (Algorithm R), + // which is an efficient technique for randomly choosing a sample of k elements + // from a larger population without replacement. + // Custom sampling function for u128 // This follows the rule of draw without replacement. + fn get_n_random_indices(range: u128, count: usize) -> Vec { + use rand::Rng; + let mut rng = Self::rng(); + + let mut result = Vec::with_capacity(count); + + if count as u128 > range { + return result; + } + + for i in 0..count as u128 { + result.push(i); + } + + for i in count as u128..range { + let random_index = rng.gen_range(0..=i); + if random_index < count as u128 { + result[random_index as usize] = i; + } + } + + result + } + + // Get `n` unique and ordered random numbers from the random number generator. pub(crate) fn get_n_random_section_starts( n: usize, max: u128, ) -> Result, DispatchError> { - let mut rng = Self::rng(); - let min_juror_stake = T::MinJurorStake::get().saturated_into::(); let mut sections_len = max.checked_div(min_juror_stake).unwrap_or(0); let last_partial_section_exists = max % min_juror_stake != 0; @@ -1137,12 +1163,14 @@ mod pallet { return Err(Error::::NotEnoughTotalJurorStakeForRandomNumberGeneration.into()); } + // Use the `get_n_random_indices` function to generate a random sample of unique indices + let random_indices = Self::get_n_random_indices(sections_len, n); + let mut random_set = BTreeSet::new(); - let indices: Vec = (0..sections_len).collect(); let last_index = sections_len.saturating_sub(1); - use rand::seq::SliceRandom; // Pick `n` unique random indices without repitition (replacement) - for &index in indices.as_slice().choose_multiple(&mut rng, n) { + for index in random_indices { + let index = index as u128; let is_last_index = index == last_index; let random_section_start = if last_partial_section_exists && is_last_index { // max is the last possible section start diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index ba1d2b8bb..46ca91c03 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -135,7 +135,7 @@ fn put_alice_in_draw(court_id: CourtId, stake: BalanceOf) { let mut draws = >::get(court_id); assert!(!draws.is_empty()); let slashable = MinJurorStake::get(); - draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; + draws[0] = Draw { juror: ALICE, weight: 2, vote: Vote::Drawn, slashable }; >::insert(court_id, draws); >::insert( ALICE, @@ -2576,9 +2576,9 @@ fn on_dispute_inserts_draws() { draws[0], Draw { juror: ALICE, - weight: 4, + weight: 2, vote: Vote::Drawn, - slashable: 4 * MinJurorStake::get() + slashable: 2 * MinJurorStake::get() } ); assert_eq!( @@ -2589,14 +2589,14 @@ fn on_dispute_inserts_draws() { draws[2], Draw { juror: CHARLIE, - weight: 6, + weight: 7, vote: Vote::Drawn, - slashable: 6 * MinJurorStake::get() + slashable: 7 * MinJurorStake::get() } ); assert_eq!( draws[3], - Draw { juror: DAVE, weight: 7, vote: Vote::Drawn, slashable: 7 * MinJurorStake::get() } + Draw { juror: DAVE, weight: 8, vote: Vote::Drawn, slashable: 8 * MinJurorStake::get() } ); assert_eq!( draws[4], From f12816585387e29cba053f36903bb73c6bf2c398 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 9 May 2023 16:23:22 +0200 Subject: [PATCH 289/368] small edits --- zrml/court/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index a38ff3b5e..97a91954b 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1121,8 +1121,6 @@ mod pallet { // The algorithm uses reservoir sampling (Algorithm R), // which is an efficient technique for randomly choosing a sample of k elements // from a larger population without replacement. - // Custom sampling function for u128 - // This follows the rule of draw without replacement. fn get_n_random_indices(range: u128, count: usize) -> Vec { use rand::Rng; let mut rng = Self::rng(); @@ -1170,7 +1168,6 @@ mod pallet { let last_index = sections_len.saturating_sub(1); // Pick `n` unique random indices without repitition (replacement) for index in random_indices { - let index = index as u128; let is_last_index = index == last_index; let random_section_start = if last_partial_section_exists && is_last_index { // max is the last possible section start From 5790e4d0eb7276e3dc34f042ae111e125b252395 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 10 May 2023 16:07:42 +0200 Subject: [PATCH 290/368] round to min juror stake, use other draw algo --- zrml/court/src/lib.rs | 156 ++++++++++++++++++++++++++-------------- zrml/court/src/tests.rs | 15 ++-- 2 files changed, 114 insertions(+), 57 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 97a91954b..338f90244 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1118,31 +1118,82 @@ mod pallet { Weight::zero() } - // The algorithm uses reservoir sampling (Algorithm R), - // which is an efficient technique for randomly choosing a sample of k elements - // from a larger population without replacement. - fn get_n_random_indices(range: u128, count: usize) -> Vec { - use rand::Rng; - let mut rng = Self::rng(); + // Returns `n` unique random numbers from `1` to `max`. + fn generate_n_unique_random_numbers( + mut rng: impl rand::Rng, + max: u128, + n: usize, + ) -> BTreeSet { + debug_assert!(max > 10_000u128 && n <= 255usize); - let mut result = Vec::with_capacity(count); + let mut swaps = BTreeMap::new(); + let mut result = BTreeSet::new(); - if count as u128 > range { - return result; - } + for i in 0..n { + let random_index = rng.gen_range(i..max as usize) as u128; + let a = *swaps.get(&i).unwrap_or(&i); + let b = *swaps.get(&random_index).unwrap_or(&random_index); - for i in 0..count as u128 { - result.push(i); + // Perform the swap + swaps.insert(i, b); + swaps.insert(random_index, a); + + result.insert(b.saturating_add(1)); } - for i in count as u128..range { - let random_index = rng.gen_range(0..=i); - if random_index < count as u128 { - result[random_index as usize] = i; + result + } + + fn generate_n_unique_random_numbers_2( + mut rng: impl rand::Rng, + max: u128, + n: usize, + ) -> BTreeSet { + debug_assert!(max > 10_000u128 && n <= 255usize); + + let mut numbers = BTreeSet::new(); + + for _ in 0..n { + let random_number = rng.gen_range(1u128..=max); + let mut current_value = random_number; + + while numbers.contains(¤t_value) { + current_value = current_value.saturating_add(1) % max; } + + numbers.insert(current_value); } - result + numbers + } + + // TODO test this function and both paths (algos) + // Return `n` unique random numbers from `1` to `max`. + pub fn get_n_random_indices(max: u128, n: usize) -> Vec { + // the collection of a vector (1u128..max) with max > 10_000 elements is too expensive + // that's why we use a different algorithm for bigger max values + let mut rng = Self::rng(); + + debug_assert!(n as u128 <= 255u128, "Ensure `n` is significantly smaller than `max`."); + if max > 10_000u128 { + // If 10% of the current total supply (1035088749687441409 / 10 = 103508874968744140) + // is divided by the current `MinJurorStake` (5000000000000), we get 20701.7749 + // so the following algorithm hits round about 5% (of current total issuance) + // locked in court + return Self::generate_n_unique_random_numbers(&mut rng, max, n) + .into_iter() + .collect::>(); + } + + debug_assert!(max <= 10_000u128, "Ensure the collected vector is not too big."); + use rand::seq::SliceRandom; + // the collection of a vector above 10_000 elements is too expensive + (1u128..=max) + .collect::>() + .as_slice() + .choose_multiple(&mut rng, n) + .cloned() + .collect::>() } // Get `n` unique and ordered random numbers from the random number generator. @@ -1151,12 +1202,9 @@ mod pallet { max: u128, ) -> Result, DispatchError> { let min_juror_stake = T::MinJurorStake::get().saturated_into::(); - let mut sections_len = max.checked_div(min_juror_stake).unwrap_or(0); - let last_partial_section_exists = max % min_juror_stake != 0; - if last_partial_section_exists { - // the last partial min_juror_stake counts as a full min_juror_stake - sections_len = sections_len.saturating_add(1); - } + debug_assert!((max % min_juror_stake).is_zero(), "This is ensured by the caller."); + let sections_len = max.checked_div(min_juror_stake).unwrap_or(0); + if sections_len < (n as u128) { return Err(Error::::NotEnoughTotalJurorStakeForRandomNumberGeneration.into()); } @@ -1165,16 +1213,8 @@ mod pallet { let random_indices = Self::get_n_random_indices(sections_len, n); let mut random_set = BTreeSet::new(); - let last_index = sections_len.saturating_sub(1); - // Pick `n` unique random indices without repitition (replacement) for index in random_indices { - let is_last_index = index == last_index; - let random_section_start = if last_partial_section_exists && is_last_index { - // max is the last possible section start - max - } else { - index.saturating_mul(min_juror_stake) - }; + let random_section_start = index.saturating_mul(min_juror_stake); random_set.insert(random_section_start); } @@ -1366,29 +1406,31 @@ mod pallet { // is increased by the random selection weight. fn get_selections( jurors: &mut JurorPoolOf, - random_set: BTreeSet, - cumulative_ranges: Vec, + random_section_starts: BTreeSet, + cumulative_section_starts: Vec<(u128, bool)>, ) -> BTreeMap> { - debug_assert!(jurors.len() == cumulative_ranges.len()); + debug_assert!(jurors.len() == cumulative_section_starts.len()); debug_assert!({ - let prev = cumulative_ranges.clone(); - let mut sorted = cumulative_ranges.clone(); + let prev = cumulative_section_starts.clone(); + let mut sorted = cumulative_section_starts.clone(); sorted.sort(); prev.len() == sorted.len() && prev.iter().zip(sorted.iter()).all(|(a, b)| a == b) }); debug_assert!({ - random_set.iter().all(|random_number| { - let last = cumulative_ranges.last().unwrap_or(&0); - *random_number <= *last + random_section_starts.iter().all(|random_section_start| { + let last = cumulative_section_starts.last().unwrap_or(&(0, false)).0; + *random_section_start <= last }) }); let mut selections = BTreeMap::>::new(); let mut invalid_juror_indices = Vec::::new(); - for random_number in random_set { - let range_index = - cumulative_ranges.binary_search(&random_number).unwrap_or_else(|i| i); + for random_section_start in random_section_starts { + let allow_zero_stake = false; + let range_index = cumulative_section_starts + .binary_search(&(random_section_start, allow_zero_stake)) + .unwrap_or_else(|i| i); if let Some(pool_item) = jurors.get_mut(range_index) { let unconsumed = pool_item.stake.saturating_sub(pool_item.consumed_stake); let lock_added = unconsumed.min(T::MinJurorStake::get()); @@ -1397,7 +1439,7 @@ mod pallet { &mut selections, &pool_item.juror, lock_added, - random_number, + random_section_start, ) { Ok(()) => {} Err(SelectionError::NoValidDelegatedJuror) => { @@ -1454,32 +1496,42 @@ mod pallet { .collect() } - // Choose `number` of jurors from the pool randomly - // according to the weighted stake of the jurors. + // Choose `number` (multiple) of `MinJurorStake` from the pool randomly + // according to the weighted stake of all jurors and delegators. // Return the random draws. pub(crate) fn choose_multiple_weighted( draw_weight: usize, ) -> Result>, DispatchError> { let mut jurors = >::get(); + let min_juror_stake = T::MinJurorStake::get().saturated_into::(); + let mut total_unconsumed = 0u128; - let mut cumulative_ranges = Vec::new(); + let mut cumulative_section_starts = Vec::new(); let mut running_total = 0u128; for pool_item in &jurors { let unconsumed = pool_item .stake .saturating_sub(pool_item.consumed_stake) .saturated_into::(); + let remainder = unconsumed % min_juror_stake; + let unconsumed = unconsumed.saturating_sub(remainder); total_unconsumed = total_unconsumed.saturating_add(unconsumed); running_total = running_total.saturating_add(unconsumed); - cumulative_ranges.push(running_total); + let zero_stake = unconsumed.is_zero(); + cumulative_section_starts.push((running_total, zero_stake)); } + debug_assert!( + (total_unconsumed % min_juror_stake).is_zero(), + "Remainders are being cut in the above for loop." + ); - let required_stake = (draw_weight as u128) - .saturating_mul(T::MinJurorStake::get().saturated_into::()); + let required_stake = (draw_weight as u128).saturating_mul(min_juror_stake); ensure!(total_unconsumed >= required_stake, Error::::NotEnoughJurorsStake); - let random_set = Self::get_n_random_section_starts(draw_weight, total_unconsumed)?; - let selections = Self::get_selections(&mut jurors, random_set, cumulative_ranges); + let random_section_starts = + Self::get_n_random_section_starts(draw_weight, total_unconsumed)?; + let selections = + Self::get_selections(&mut jurors, random_section_starts, cumulative_section_starts); >::put(jurors); Ok(Self::convert_selections_to_draws(selections)) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 46ca91c03..eb879c84f 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -135,7 +135,7 @@ fn put_alice_in_draw(court_id: CourtId, stake: BalanceOf) { let mut draws = >::get(court_id); assert!(!draws.is_empty()); let slashable = MinJurorStake::get(); - draws[0] = Draw { juror: ALICE, weight: 2, vote: Vote::Drawn, slashable }; + draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(court_id, draws); >::insert( ALICE, @@ -2576,9 +2576,9 @@ fn on_dispute_inserts_draws() { draws[0], Draw { juror: ALICE, - weight: 2, + weight: 3, vote: Vote::Drawn, - slashable: 2 * MinJurorStake::get() + slashable: 3 * MinJurorStake::get() } ); assert_eq!( @@ -2596,11 +2596,16 @@ fn on_dispute_inserts_draws() { ); assert_eq!( draws[3], - Draw { juror: DAVE, weight: 8, vote: Vote::Drawn, slashable: 8 * MinJurorStake::get() } + Draw { juror: DAVE, weight: 6, vote: Vote::Drawn, slashable: 6 * MinJurorStake::get() } ); assert_eq!( draws[4], - Draw { juror: EVE, weight: 9, vote: Vote::Drawn, slashable: 9 * MinJurorStake::get() } + Draw { + juror: EVE, + weight: 10, + vote: Vote::Drawn, + slashable: 10 * MinJurorStake::get() + } ); assert_eq!(draws.len(), 5usize); }); From 0bba8ce7ad75701679df31e303f0f4fccc5856b0 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Thu, 11 May 2023 13:54:58 +0200 Subject: [PATCH 291/368] use partial fisher yates algorithm --- zrml/court/src/lib.rs | 126 +++++++++++--------------------------- zrml/court/src/tests.rs | 6 +- zrml/court/src/weights.rs | 118 ++++++++++++++++++----------------- 3 files changed, 101 insertions(+), 149 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 338f90244..f24f249e7 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -494,7 +494,7 @@ mod pallet { /// Join the court to become a delegator. /// The `amount` of this call represents the total stake of the delegator. - /// If the one of the delegated jurors is selected for a court case, + /// If the random selection algorithm chooses a delegators stake, /// the caller delegates the vote power to the drawn delegated juror. /// The delegator gets slashed or rewarded according to the delegated jurors decisions. /// @@ -1118,89 +1118,18 @@ mod pallet { Weight::zero() } - // Returns `n` unique random numbers from `1` to `max`. - fn generate_n_unique_random_numbers( - mut rng: impl rand::Rng, - max: u128, - n: usize, - ) -> BTreeSet { - debug_assert!(max > 10_000u128 && n <= 255usize); - - let mut swaps = BTreeMap::new(); - let mut result = BTreeSet::new(); - - for i in 0..n { - let random_index = rng.gen_range(i..max as usize) as u128; - let a = *swaps.get(&i).unwrap_or(&i); - let b = *swaps.get(&random_index).unwrap_or(&random_index); - - // Perform the swap - swaps.insert(i, b); - swaps.insert(random_index, a); - - result.insert(b.saturating_add(1)); - } - - result - } - - fn generate_n_unique_random_numbers_2( - mut rng: impl rand::Rng, - max: u128, - n: usize, - ) -> BTreeSet { - debug_assert!(max > 10_000u128 && n <= 255usize); - - let mut numbers = BTreeSet::new(); - - for _ in 0..n { - let random_number = rng.gen_range(1u128..=max); - let mut current_value = random_number; - - while numbers.contains(¤t_value) { - current_value = current_value.saturating_add(1) % max; - } - - numbers.insert(current_value); - } - - numbers - } - - // TODO test this function and both paths (algos) - // Return `n` unique random numbers from `1` to `max`. - pub fn get_n_random_indices(max: u128, n: usize) -> Vec { - // the collection of a vector (1u128..max) with max > 10_000 elements is too expensive - // that's why we use a different algorithm for bigger max values - let mut rng = Self::rng(); - - debug_assert!(n as u128 <= 255u128, "Ensure `n` is significantly smaller than `max`."); - if max > 10_000u128 { - // If 10% of the current total supply (1035088749687441409 / 10 = 103508874968744140) - // is divided by the current `MinJurorStake` (5000000000000), we get 20701.7749 - // so the following algorithm hits round about 5% (of current total issuance) - // locked in court - return Self::generate_n_unique_random_numbers(&mut rng, max, n) - .into_iter() - .collect::>(); - } - - debug_assert!(max <= 10_000u128, "Ensure the collected vector is not too big."); - use rand::seq::SliceRandom; - // the collection of a vector above 10_000 elements is too expensive - (1u128..=max) - .collect::>() - .as_slice() - .choose_multiple(&mut rng, n) - .cloned() - .collect::>() - } - - // Get `n` unique and ordered random numbers from the random number generator. + // Get `n` unique and ordered random `MinJurorStake` section starts + // from the random number generator. + // Uses Partial Fisher Yates shuffle and drawing without replacement. + // The time complexity is O(n). + // Return a vector of n unique random numbers between 1 and max (inclusive). pub(crate) fn get_n_random_section_starts( n: usize, max: u128, ) -> Result, DispatchError> { + use rand::Rng; + let mut rng = Self::rng(); + let min_juror_stake = T::MinJurorStake::get().saturated_into::(); debug_assert!((max % min_juror_stake).is_zero(), "This is ensured by the caller."); let sections_len = max.checked_div(min_juror_stake).unwrap_or(0); @@ -1209,18 +1138,31 @@ mod pallet { return Err(Error::::NotEnoughTotalJurorStakeForRandomNumberGeneration.into()); } - // Use the `get_n_random_indices` function to generate a random sample of unique indices - let random_indices = Self::get_n_random_indices(sections_len, n); + let mut swaps = BTreeMap::::new(); + let mut random_section_starts = BTreeSet::new(); + + for i in 0..(n as u128) { + let visited_i = *swaps.get(&i).unwrap_or(&i); + + let unused_random_index = rng.gen_range(i..sections_len); + let unused_random_number = + *swaps.get(&unused_random_index).unwrap_or(&unused_random_index); + + // save the unused random number, which is between i and sections_len, to the map + // i can be found later on two, because we save it below as `visited_i` + swaps.insert(i, unused_random_number); + // save already visited i to the map, so that it can possibly inserted later on + swaps.insert(unused_random_index, visited_i); - let mut random_set = BTreeSet::new(); - for index in random_indices { - let random_section_start = index.saturating_mul(min_juror_stake); - random_set.insert(random_section_start); + // add one because we need numbers between 1 and sections_len (inclusive) + let random_index = unused_random_number.saturating_add(1); + let random_section_start = random_index.saturating_mul(min_juror_stake); + random_section_starts.insert(random_section_start); } - debug_assert!(random_set.len() == n); + debug_assert!(random_section_starts.len() == n); - Ok(random_set) + Ok(random_section_starts) } // Adds active lock amount. @@ -1496,8 +1438,10 @@ mod pallet { .collect() } - // Choose `number` (multiple) of `MinJurorStake` from the pool randomly + // Choose `draw_weight` (multiple) of `MinJurorStake` from the pool randomly // according to the weighted stake of all jurors and delegators. + // NOTE: The jurors and delegators are being cut by the remainder + // if the stake is not a multiple of `MinJurorStake`. // Return the random draws. pub(crate) fn choose_multiple_weighted( draw_weight: usize, @@ -1518,6 +1462,10 @@ mod pallet { let unconsumed = unconsumed.saturating_sub(remainder); total_unconsumed = total_unconsumed.saturating_add(unconsumed); running_total = running_total.saturating_add(unconsumed); + // this is useful for binary search to match the correct juror + // if we don't do this, the binary search in `get_selections` + // might take the wrong juror (with zero stake) + // (running total would be the same for two consecutive jurors) let zero_stake = unconsumed.is_zero(); cumulative_section_starts.push((running_total, zero_stake)); } diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index eb879c84f..ecb6c1609 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -2589,14 +2589,14 @@ fn on_dispute_inserts_draws() { draws[2], Draw { juror: CHARLIE, - weight: 7, + weight: 6, vote: Vote::Drawn, - slashable: 7 * MinJurorStake::get() + slashable: 6 * MinJurorStake::get() } ); assert_eq!( draws[3], - Draw { juror: DAVE, weight: 6, vote: Vote::Drawn, slashable: 6 * MinJurorStake::get() } + Draw { juror: DAVE, weight: 7, vote: Vote::Drawn, slashable: 7 * MinJurorStake::get() } ); assert_eq!( draws[4], diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 12bf05fbd..1edafbe37 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for zrml_court //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-28, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-11, STEPS: `10`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -74,9 +74,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn join_court(j: u32) -> Weight { - Weight::from_ref_time(35_400_000) + Weight::from_ref_time(33_951_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(103_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(94_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -84,11 +84,11 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:6 w:1) // Storage: Balances Locks (r:1 w:1) fn delegate(j: u32, d: u32) -> Weight { - Weight::from_ref_time(34_177_000) + Weight::from_ref_time(46_155_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(134_000).saturating_mul(j.into())) - // Standard Error: 82_000 - .saturating_add(Weight::from_ref_time(2_902_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(122_000).saturating_mul(j.into())) + // Standard Error: 51_000 + .saturating_add(Weight::from_ref_time(863_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -96,32 +96,32 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) fn prepare_exit_court(j: u32) -> Weight { - Weight::from_ref_time(16_132_000) + Weight::from_ref_time(19_325_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(97_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(84_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn exit_court_remove() -> Weight { - Weight::from_ref_time(43_000_000) + Weight::from_ref_time(38_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Jurors (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn exit_court_set() -> Weight { - Weight::from_ref_time(41_000_000) + Weight::from_ref_time(37_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) fn vote(d: u32) -> Weight { - Weight::from_ref_time(66_992_000) + Weight::from_ref_time(48_629_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(51_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(90_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -131,9 +131,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) fn denounce_vote(d: u32) -> Weight { - Weight::from_ref_time(40_828_000) + Weight::from_ref_time(41_779_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(132_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(126_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -143,9 +143,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Courts (r:1 w:0) // Storage: Court SelectedDraws (r:1 w:1) fn reveal_vote(d: u32) -> Weight { - Weight::from_ref_time(76_131_000) + Weight::from_ref_time(69_471_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(85_000).saturating_mul(d.into())) + .saturating_add(Weight::from_ref_time(92_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -156,18 +156,20 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:201 w:198) + // Storage: Court Jurors (r:223 w:222) // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:2 w:2) // Storage: Balances Reserves (r:1 w:1) - fn appeal(j: u32, a: u32, _r: u32, _f: u32) -> Weight { + fn appeal(j: u32, a: u32, r: u32, _f: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 36_000 - .saturating_add(Weight::from_ref_time(7_314_000).saturating_mul(j.into())) - // Standard Error: 10_623_000 - .saturating_add(Weight::from_ref_time(3_108_529_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads((119_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes((119_u64).saturating_mul(a.into()))) + // Standard Error: 26_000 + .saturating_add(Weight::from_ref_time(5_584_000).saturating_mul(j.into())) + // Standard Error: 7_923_000 + .saturating_add(Weight::from_ref_time(2_539_125_000).saturating_mul(a.into())) + // Standard Error: 320_000 + .saturating_add(Weight::from_ref_time(1_503_000).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads((128_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes((128_u64).saturating_mul(a.into()))) } // Storage: Court Courts (r:1 w:1) // Storage: Court SelectedDraws (r:1 w:1) @@ -175,8 +177,8 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: System Account (r:6 w:5) fn reassign_juror_stakes(d: u32) -> Weight { Weight::from_ref_time(0) - // Standard Error: 18_000 - .saturating_add(Weight::from_ref_time(45_034_000).saturating_mul(d.into())) + // Standard Error: 19_000 + .saturating_add(Weight::from_ref_time(44_416_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -184,7 +186,7 @@ impl WeightInfoZeitgeist for WeightInfo { } // Storage: Court YearlyInflation (r:0 w:1) fn set_inflation() -> Weight { - Weight::from_ref_time(6_000_000).saturating_add(T::DbWeight::get().writes(1)) + Weight::from_ref_time(16_000_000).saturating_add(T::DbWeight::get().writes(1)) } // Storage: Court YearlyInflation (r:1 w:0) // Storage: Court JurorPool (r:1 w:0) @@ -192,7 +194,7 @@ impl WeightInfoZeitgeist for WeightInfo { fn handle_inflation(j: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(13_129_000).saturating_mul(j.into())) + .saturating_add(Weight::from_ref_time(12_853_000).saturating_mul(j.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) @@ -200,33 +202,35 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:31 w:30) + // Storage: Court Jurors (r:32 w:31) fn select_jurors(a: u32) -> Weight { - Weight::from_ref_time(531_137_000) - // Standard Error: 7_595_000 - .saturating_add(Weight::from_ref_time(1_598_167_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(20)) - .saturating_add(T::DbWeight::get().reads((61_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(18)) - .saturating_add(T::DbWeight::get().writes((61_u64).saturating_mul(a.into()))) + Weight::from_ref_time(513_126_000) + // Standard Error: 7_829_000 + .saturating_add(Weight::from_ref_time(1_640_272_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(21)) + .saturating_add(T::DbWeight::get().reads((60_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(19)) + .saturating_add(T::DbWeight::get().writes((60_u64).saturating_mul(a.into()))) } // Storage: Court NextCourtId (r:1 w:1) // Storage: Court JurorPool (r:1 w:1) // Storage: Court JurorsSelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:19 w:19) + // Storage: Court Jurors (r:23 w:23) // Storage: Court RequestBlock (r:1 w:0) // Storage: PredictionMarkets MarketIdsPerDisputeBlock (r:1 w:1) // Storage: Court SelectedDraws (r:0 w:1) // Storage: Court CourtIdToMarketId (r:0 w:1) // Storage: Court MarketIdToCourtId (r:0 w:1) // Storage: Court Courts (r:0 w:1) - fn on_dispute(j: u32, _r: u32) -> Weight { - Weight::from_ref_time(185_293_000) + fn on_dispute(j: u32, r: u32) -> Weight { + Weight::from_ref_time(196_514_000) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(376_000).saturating_mul(j.into())) - .saturating_add(T::DbWeight::get().reads(30)) - .saturating_add(T::DbWeight::get().writes(32)) + .saturating_add(Weight::from_ref_time(144_000).saturating_mul(j.into())) + // Standard Error: 3_000 + .saturating_add(Weight::from_ref_time(157_000).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(33)) + .saturating_add(T::DbWeight::get().writes(35)) } // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:1) @@ -235,9 +239,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: MarketCommons Markets (r:1 w:0) // Storage: Court Jurors (r:1 w:1) fn on_resolution(d: u32) -> Weight { - Weight::from_ref_time(13_229_000) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(4_108_000).saturating_mul(d.into())) + Weight::from_ref_time(17_329_000) + // Standard Error: 1_000 + .saturating_add(Weight::from_ref_time(4_102_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(1)) @@ -248,9 +252,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Balances Reserves (r:1 w:1) // Storage: System Account (r:1 w:1) fn exchange(a: u32) -> Weight { - Weight::from_ref_time(16_280_000) + Weight::from_ref_time(17_021_000) // Standard Error: 29_000 - .saturating_add(Weight::from_ref_time(22_041_000).saturating_mul(a.into())) + .saturating_add(Weight::from_ref_time(21_348_000).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(a.into()))) @@ -266,18 +270,18 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court CourtIdToMarketId (r:1 w:0) // Storage: MarketCommons Markets (r:1 w:0) fn has_failed() -> Weight { - Weight::from_ref_time(23_000_000).saturating_add(T::DbWeight::get().reads(5)) + Weight::from_ref_time(24_000_000).saturating_add(T::DbWeight::get().reads(5)) } // Storage: Court MarketIdToCourtId (r:1 w:0) // Storage: Court Courts (r:1 w:1) // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:510 w:510) fn on_global_dispute(a: u32, d: u32) -> Weight { - Weight::from_ref_time(0) - // Standard Error: 518_000 - .saturating_add(Weight::from_ref_time(6_098_000).saturating_mul(a.into())) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(4_261_000).saturating_mul(d.into())) + Weight::from_ref_time(11_646_000) + // Standard Error: 588_000 + .saturating_add(Weight::from_ref_time(20_187_000).saturating_mul(a.into())) + // Standard Error: 5_000 + .saturating_add(Weight::from_ref_time(4_083_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -288,9 +292,9 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court Jurors (r:1 w:1) // Storage: Court Courts (r:0 w:1) fn clear(d: u32) -> Weight { - Weight::from_ref_time(0) - // Standard Error: 9_000 - .saturating_add(Weight::from_ref_time(4_845_000).saturating_mul(d.into())) + Weight::from_ref_time(4_229_000) + // Standard Error: 0 + .saturating_add(Weight::from_ref_time(4_115_000).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d.into()))) .saturating_add(T::DbWeight::get().writes(2)) From 631ad9b5c53effc2d0df7d1e612816baf115e030 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 12 May 2023 14:56:36 +0200 Subject: [PATCH 292/368] improve naming and comments --- primitives/src/constants/mock.rs | 2 +- runtime/battery-station/src/parameters.rs | 2 +- runtime/common/src/lib.rs | 2 +- runtime/zeitgeist/src/parameters.rs | 2 +- zrml/court/src/benchmarks.rs | 120 ++--- zrml/court/src/lib.rs | 546 ++++++++++++---------- zrml/court/src/mock.rs | 6 +- zrml/court/src/tests.rs | 463 ++++++++++-------- zrml/court/src/types.rs | 29 +- zrml/court/src/weights.rs | 4 +- zrml/prediction-markets/src/mock.rs | 16 +- zrml/prediction-markets/src/tests.rs | 27 +- 12 files changed, 667 insertions(+), 552 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index a6a04d1fc..409cd830c 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -46,7 +46,7 @@ parameter_types! { pub const MaxAppeals: u32 = 4; pub const MaxDelegations: u32 = 5; pub const MaxSelectedDraws: u32 = 510; - pub const MaxJurors: u32 = 1_000; + pub const MaxCourtParticipants: u32 = 1_000; pub const MinJurorStake: Balance = 50 * CENT; pub const InflationPeriod: BlockNumber = 20; } diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 1d096dcf5..0edaee45e 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -102,7 +102,7 @@ parameter_types! { /// The maximum number of randomly selected `MinJurorStake` draws / atoms of jurors for a dispute. pub const MaxSelectedDraws: u32 = 510; /// The maximum number of jurors / delegators that can be registered. - pub const MaxJurors: u32 = 1_000; + pub const MaxCourtParticipants: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. pub const MinJurorStake: Balance = 500 * BASE; /// The interval for requesting multiple court votes at once. diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index afb22f0b8..302101878 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -996,7 +996,7 @@ macro_rules! impl_config_traits { type MaxAppeals = MaxAppeals; type MaxDelegations = MaxDelegations; type MaxSelectedDraws = MaxSelectedDraws; - type MaxJurors = MaxJurors; + type MaxCourtParticipants = MaxCourtParticipants; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; type Random = RandomnessCollectiveFlip; diff --git a/runtime/zeitgeist/src/parameters.rs b/runtime/zeitgeist/src/parameters.rs index 101a71c1c..be18d5aa0 100644 --- a/runtime/zeitgeist/src/parameters.rs +++ b/runtime/zeitgeist/src/parameters.rs @@ -102,7 +102,7 @@ parameter_types! { /// The maximum number of randomly selected `MinJurorStake` draws / atoms of jurors for a dispute. pub const MaxSelectedDraws: u32 = 510; /// The maximum number of jurors / delegators that can be registered. - pub const MaxJurors: u32 = 1_000; + pub const MaxCourtParticipants: u32 = 1_000; /// The minimum stake a user needs to reserve to become a juror. pub const MinJurorStake: Balance = 500 * BASE; /// The interval for requesting multiple court votes at once. diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 6372c8ae8..d42853c7a 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -24,9 +24,10 @@ extern crate alloc; use crate::{ - types::{CourtStatus, Draw, JurorInfo, JurorPoolItem, Vote}, - AppealInfo, BalanceOf, Call, Config, CourtId, Courts, DelegatedStakesOf, JurorPool, Jurors, - MarketIdToCourtId, MarketOf, Pallet as Court, Pallet, RequestBlock, SelectedDraws, VoteItem, + types::{CourtParticipantInfo, CourtPoolItem, CourtStatus, Draw, Vote}, + AppealInfo, BalanceOf, Call, Config, CourtId, CourtPool, Courts, DelegatedStakesOf, + MarketIdToCourtId, MarketOf, Pallet as Court, Pallet, Participants, RequestBlock, + SelectedDraws, VoteItem, }; use alloc::{vec, vec::Vec}; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; @@ -92,16 +93,16 @@ fn fill_pool(number: u32) -> Result<(), &'static str> where T: Config, { - let mut jurors = >::get(); + let mut pool = >::get(); let min_amount = T::MinJurorStake::get(); let max_amount = min_amount + min_amount + BalanceOf::::from(number); for i in 0..number { let juror: T::AccountId = account("juror", i, 0); let stake = max_amount - BalanceOf::::from(i); let _ = T::Currency::deposit_creating(&juror, stake); - >::insert( + >::insert( juror.clone(), - JurorInfo { + CourtParticipantInfo { stake, active_lock: >::zero(), prepare_exit_at: None, @@ -109,15 +110,15 @@ where }, ); let consumed_stake = BalanceOf::::zero(); - let pool_item = JurorPoolItem { stake, juror: juror.clone(), consumed_stake }; - match jurors - .binary_search_by_key(&(stake, &juror), |pool_item| (pool_item.stake, &pool_item.juror)) - { + let pool_item = CourtPoolItem { stake, court_participant: juror.clone(), consumed_stake }; + match pool.binary_search_by_key(&(stake, &juror), |pool_item| { + (pool_item.stake, &pool_item.court_participant) + }) { Ok(_) => panic!("Juror already in pool"), - Err(index) => jurors.try_insert(index, pool_item).unwrap(), + Err(index) => pool.try_insert(index, pool_item).unwrap(), }; } - >::put(jurors); + >::put(pool); Ok(()) } @@ -127,19 +128,19 @@ fn fill_delegations() where T: Config, { - let jurors = >::get(); - debug_assert!(jurors.len() >= T::MaxDelegations::get() as usize); - let mut jurors_iter = jurors.iter(); + let pool = >::get(); + debug_assert!(pool.len() >= T::MaxDelegations::get() as usize); + let mut pool_iter = pool.iter(); let mut delegated_jurors = vec![]; for _ in 0..T::MaxDelegations::get() { - let delegated_juror = jurors_iter.next().unwrap().juror.clone(); + let delegated_juror = pool_iter.next().unwrap().court_participant.clone(); delegated_jurors.push(delegated_juror); } - for pool_item in jurors_iter { - let juror = &pool_item.juror; - let mut j = >::get(juror).unwrap(); + for pool_item in pool_iter { + let juror = &pool_item.court_participant; + let mut j = >::get(juror).unwrap(); j.delegations = Some(delegated_jurors.clone().try_into().unwrap()); - >::insert(juror, j); + >::insert(juror, j); } } @@ -180,19 +181,23 @@ where for i in 0..number { let juror = account("juror", i, 0); deposit::(&juror); - >::insert( + >::insert( &juror, - JurorInfo { + CourtParticipantInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, delegations: Default::default(), }, ); - let draw = - Draw { juror, vote: Vote::Drawn, weight: 1u32, slashable: T::MinJurorStake::get() }; + let draw = Draw { + court_participant: juror, + vote: Vote::Drawn, + weight: 1u32, + slashable: T::MinJurorStake::get(), + }; let index = draws - .binary_search_by_key(&draw.juror, |draw| draw.juror.clone()) + .binary_search_by_key(&draw.court_participant, |draw| draw.court_participant.clone()) .unwrap_or_else(|j| j); draws.try_insert(index, draw).unwrap(); } @@ -209,7 +214,8 @@ where // change draws to have revealed votes for draw in draws.iter_mut() { let salt = Default::default(); - let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); + let commitment = + T::Hashing::hash_of(&(draw.court_participant.clone(), winner_outcome.clone(), salt)); draw.vote = Vote::Revealed { commitment, vote_item: VoteItem::Outcome(winner_outcome.clone()), @@ -221,7 +227,7 @@ where benchmarks! { join_court { - let j in 0..(T::MaxJurors::get() - 1); + let j in 0..(T::MaxCourtParticipants::get() - 1); fill_pool::(j)?; @@ -235,7 +241,7 @@ benchmarks! { delegate { // jurors greater or equal to MaxDelegations, // because we can not delegate to a non-existent juror - let j in 5..(T::MaxJurors::get() - 1); + let j in 5..(T::MaxCourtParticipants::get() - 1); let d in 1..T::MaxDelegations::get(); fill_pool::(j)?; @@ -243,18 +249,18 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); join_with_min_stake::(&caller)?; - let juror_pool = >::get(); + let juror_pool = >::get(); let mut delegations = Vec::::new(); juror_pool.iter() - .filter(|pool_item| pool_item.juror != caller).take(d as usize) - .for_each(|pool_item| delegations.push(pool_item.juror.clone())); + .filter(|pool_item| pool_item.court_participant != caller).take(d as usize) + .for_each(|pool_item| delegations.push(pool_item.court_participant.clone())); let new_stake = T::MinJurorStake::get() .saturating_add(1u128.saturated_into::>()); }: _(RawOrigin::Signed(caller), new_stake, delegations) prepare_exit_court { - let j in 0..(T::MaxJurors::get() - 1); + let j in 0..(T::MaxCourtParticipants::get() - 1); fill_pool::(j)?; @@ -270,8 +276,8 @@ benchmarks! { let now = >::block_number(); >::set_block_number(now + T::InflationPeriod::get()); - >::mutate(caller.clone(), |prev_juror_info| { - prev_juror_info.as_mut().unwrap().active_lock = >::zero(); + >::mutate(caller.clone(), |prev_p_info| { + prev_p_info.as_mut().unwrap().active_lock = >::zero(); }); let juror = T::Lookup::unlookup(caller.clone()); @@ -285,8 +291,8 @@ benchmarks! { let now = >::block_number(); >::set_block_number(now + T::InflationPeriod::get()); - >::mutate(caller.clone(), |prev_juror_info| { - prev_juror_info.as_mut().unwrap().active_lock = T::MinJurorStake::get(); + >::mutate(caller.clone(), |prev_p_info| { + prev_p_info.as_mut().unwrap().active_lock = T::MinJurorStake::get(); }); let juror = T::Lookup::unlookup(caller.clone()); @@ -295,7 +301,7 @@ benchmarks! { vote { let d in 1..T::MaxSelectedDraws::get(); - fill_pool::(T::MaxJurors::get() - 1)?; + fill_pool::(T::MaxCourtParticipants::get() - 1)?; let caller: T::AccountId = whitelisted_caller(); let (market_id, court_id) = setup_court::()?; @@ -309,12 +315,12 @@ benchmarks! { let draws_len = draws.len(); draws.remove(0); let draw = Draw { - juror: caller.clone(), + court_participant: caller.clone(), vote: Vote::Drawn, weight: 1u32, slashable: >::zero(), }; - let index = draws.binary_search_by_key(&caller, |draw| draw.juror.clone()).unwrap_or_else(|j| j); + let index = draws.binary_search_by_key(&caller, |draw| draw.court_participant.clone()).unwrap_or_else(|j| j); draws.try_insert(index, draw).unwrap(); >::insert(court_id, draws); @@ -342,7 +348,7 @@ benchmarks! { let vote_item = VoteItem::Outcome(outcome); let denounced_juror: T::AccountId = account("denounced_juror", 0, 0); join_with_min_stake::(&denounced_juror)?; - >::insert(&denounced_juror, JurorInfo { + >::insert(&denounced_juror, CourtParticipantInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, @@ -354,9 +360,9 @@ benchmarks! { let mut draws = >::get(court_id); draws.remove(0); let draws_len = draws.len(); - let index = draws.binary_search_by_key(&denounced_juror, |draw| draw.juror.clone()).unwrap_or_else(|j| j); + let index = draws.binary_search_by_key(&denounced_juror, |draw| draw.court_participant.clone()).unwrap_or_else(|j| j); let draw = Draw { - juror: denounced_juror, + court_participant: denounced_juror, vote: Vote::Secret { commitment }, weight: 1u32, slashable: T::MinJurorStake::get(), @@ -370,7 +376,7 @@ benchmarks! { reveal_vote { let d in 1..T::MaxSelectedDraws::get(); - fill_pool::(T::MaxJurors::get() - 1)?; + fill_pool::(T::MaxCourtParticipants::get() - 1)?; let caller: T::AccountId = whitelisted_caller(); let (market_id, court_id) = setup_court::()?; @@ -384,7 +390,7 @@ benchmarks! { let outcome = OutcomeReport::Scalar(0u128); let vote_item = VoteItem::Outcome(outcome); join_with_min_stake::(&caller)?; - >::insert(&caller, JurorInfo { + >::insert(&caller, CourtParticipantInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, @@ -395,9 +401,9 @@ benchmarks! { let mut draws = >::get(court_id); let draws_len = draws.len(); draws.remove(0); - let index = draws.binary_search_by_key(&caller, |draw| draw.juror.clone()).unwrap_or_else(|j| j); + let index = draws.binary_search_by_key(&caller, |draw| draw.court_participant.clone()).unwrap_or_else(|j| j); draws.try_insert(index, Draw { - juror: caller.clone(), + court_participant: caller.clone(), vote: Vote::Secret { commitment }, weight: 1u32, slashable: T::MinJurorStake::get(), @@ -409,7 +415,7 @@ benchmarks! { appeal { // from 255 because in the last appeal round we need at least 255 jurors - let j in 255..T::MaxJurors::get(); + let j in 255..T::MaxCourtParticipants::get(); let a in 0..(T::MaxAppeals::get() - 2); let r in 0..62; let f in 0..62; @@ -449,7 +455,7 @@ benchmarks! { let draws_len = Court::::necessary_draws_weight(a as usize) as u32; for i in 0..draws_len { let juror: T::AccountId = account("juror", i, 0); - >::insert(&juror, JurorInfo { + >::insert(&juror, CourtParticipantInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, @@ -459,7 +465,7 @@ benchmarks! { let commitment = T::Hashing::hash_of(&(juror.clone(), vote_item.clone(), salt)); let draw = Draw { - juror, + court_participant: juror, vote: Vote::Revealed { commitment, vote_item, salt }, weight: 1u32, slashable: >::zero() @@ -486,7 +492,7 @@ benchmarks! { assert_eq!(court.cycle_ends.appeal, new_resolve_at); } - reassign_juror_stakes { + reassign_court_stakes { // because we have 5 MaxDelegations let d in 5..T::MaxSelectedDraws::get(); debug_assert!(T::MaxDelegations::get() < T::MaxSelectedDraws::get()); @@ -514,7 +520,7 @@ benchmarks! { for i in 0..d { let juror: T::AccountId = account("juror_i", i, 0); deposit::(&juror); - >::insert(&juror, JurorInfo { + >::insert(&juror, CourtParticipantInfo { stake: T::MinJurorStake::get(), active_lock: T::MinJurorStake::get(), prepare_exit_at: None, @@ -530,14 +536,14 @@ benchmarks! { }; let commitment = T::Hashing::hash_of(&(juror.clone(), vote_item.clone(), salt)); Draw { - juror, + court_participant: juror, vote: Vote::Revealed { commitment, vote_item, salt }, weight: 1u32, slashable: T::MinJurorStake::get(), } } else { Draw { - juror, + court_participant: juror, vote: Vote::Delegated { delegated_stakes: delegated_stakes.clone() }, weight: 1u32, slashable: T::MinJurorStake::get(), @@ -553,7 +559,7 @@ benchmarks! { }: _(RawOrigin::Root, inflation) handle_inflation { - let j in 1..T::MaxJurors::get(); + let j in 1..T::MaxCourtParticipants::get(); fill_pool::(j)?; >::set_block_number(T::InflationPeriod::get()); @@ -564,7 +570,7 @@ benchmarks! { select_jurors { let a in 0..(T::MaxAppeals::get() - 1); - fill_pool::(T::MaxJurors::get())?; + fill_pool::(T::MaxCourtParticipants::get())?; fill_delegations::(); }: { @@ -572,7 +578,7 @@ benchmarks! { } on_dispute { - let j in 31..T::MaxJurors::get(); + let j in 31..T::MaxCourtParticipants::get(); let r in 0..62; let now = >::block_number(); @@ -614,7 +620,7 @@ benchmarks! { // change draws to have revealed votes for draw in draws.iter_mut() { let salt = Default::default(); - let commitment = T::Hashing::hash_of(&(draw.juror.clone(), winner_outcome.clone(), salt)); + let commitment = T::Hashing::hash_of(&(draw.court_participant.clone(), winner_outcome.clone(), salt)); draw.vote = Vote::Revealed { commitment, vote_item: VoteItem::Outcome(winner_outcome.clone()), diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index f24f249e7..86f11fc78 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -25,9 +25,9 @@ extern crate alloc; use crate::{ traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, weights::WeightInfoZeitgeist, - AppealInfo, CommitmentMatcher, CourtId, CourtInfo, CourtStatus, Draw, JurorInfo, JurorPoolItem, - JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, SelectionError, SelectionValue, - SelfInfo, Vote, VoteItem, VoteItemType, + AppealInfo, CommitmentMatcher, CourtId, CourtInfo, CourtParticipantInfo, CourtPoolItem, + CourtStatus, Draw, JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, + SelectionError, SelectionValue, SelfInfo, Vote, VoteItem, VoteItemType, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -150,11 +150,18 @@ mod pallet { Currency = Self::Currency, >; - /// The maximum number of appeals until the court fails. + /// The maximum number of appeals until a court fails. #[pallet::constant] type MaxAppeals: Get; - /// The maximum number of randomly selected jurors for a dispute. + /// The maximum number of randomly selected n * `MinJurorStake` (n equals all draw weights) + /// out of all jurors and delegators stake. This configuration parameter should be + /// the maximum necessary_draws_weight multiplied by 2. + /// (necessary_draws_weight = 2^(appeals_len) * 31 + 2^(appeals_len) - 1) + /// Assume MaxAppeals - 1 (= 3), example: 2^3 * 31 + 2^3 - 1 = 255 + /// => 2 * 255 = 510 = `MaxSelectedDraws`. + /// Why the multiplication by two? Because with delegations each juror draw weight + /// could potentially delegate an additional juror in addition to the delegator itself. #[pallet::constant] type MaxSelectedDraws: Get; @@ -162,15 +169,15 @@ mod pallet { #[pallet::constant] type MaxDelegations: Get; - /// The maximum number of jurors that can be registered. + /// The maximum number of jurors and delegators that can be registered. #[pallet::constant] - type MaxJurors: Get; + type MaxCourtParticipants: Get; - /// The minimum stake a user needs to reserve to become a juror. + /// The minimum stake a user needs to lock to become a juror. #[pallet::constant] type MinJurorStake: Get>; - /// The origin for monetary governance + /// The origin for monetary governance to control the court inflation. type MonetaryGovernanceOrigin: EnsureOrigin; /// Randomness source @@ -228,9 +235,11 @@ mod pallet { pub(crate) type DelegationsOf = BoundedVec, ::MaxDelegations>; pub(crate) type VoteOf = Vote, DelegatedStakesOf>; pub(crate) type JurorVoteWithStakesOf = JurorVoteWithStakes, BalanceOf>; - pub(crate) type JurorInfoOf = JurorInfo, BlockNumberFor, DelegationsOf>; - pub(crate) type JurorPoolItemOf = JurorPoolItem, BalanceOf>; - pub(crate) type JurorPoolOf = BoundedVec, ::MaxJurors>; + pub(crate) type CourtParticipantInfoOf = + CourtParticipantInfo, BlockNumberFor, DelegationsOf>; + pub(crate) type CourtPoolItemOf = CourtPoolItem, BalanceOf>; + pub(crate) type CourtPoolOf = + BoundedVec, ::MaxCourtParticipants>; pub(crate) type DrawOf = Draw, BalanceOf, HashOf, DelegatedStakesOf>; pub(crate) type SelectedDrawsOf = BoundedVec, ::MaxSelectedDraws>; pub(crate) type AppealOf = AppealInfo, BalanceOf>; @@ -243,21 +252,22 @@ mod pallet { #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); - /// The pool of jurors who can get randomly selected according to their stake. + /// The pool of jurors and delegators who can get randomly selected according to their stake. /// The pool is sorted by stake in ascending order [min, ..., max]. #[pallet::storage] - pub type JurorPool = StorageValue<_, JurorPoolOf, ValueQuery>; + pub type CourtPool = StorageValue<_, CourtPoolOf, ValueQuery>; - /// The general information about each juror. + /// The general information about each juror and delegator. #[pallet::storage] - pub type Jurors = - StorageMap<_, Blake2_128Concat, T::AccountId, JurorInfoOf, OptionQuery>; + pub type Participants = + StorageMap<_, Blake2_128Concat, T::AccountId, CourtParticipantInfoOf, OptionQuery>; - /// An extra layer of pseudo randomness. + /// An extra layer of pseudo randomness so that we can generate a new random seed with it. #[pallet::storage] - pub type JurorsSelectionNonce = StorageValue<_, u64, ValueQuery>; + pub type SelectionNonce = StorageValue<_, u64, ValueQuery>; - /// The randomly selected jurors with their vote. + /// The randomly selected jurors and delegators, their vote weight, + /// the status about their vote and their selected and risked funds. #[pallet::storage] pub type SelectedDraws = StorageMap<_, Blake2_128Concat, CourtId, SelectedDrawsOf, ValueQuery>; @@ -280,7 +290,7 @@ mod pallet { pub type CourtIdToMarketId = StorageMap<_, Twox64Concat, CourtId, MarketIdOf, OptionQuery>; - /// The block number in the future when jurors should start voting. + /// The future block number when jurors should start voting. /// This is useful for the user experience of the jurors to vote for multiple courts at once. #[pallet::storage] pub type RequestBlock = StorageValue<_, T::BlockNumber, ValueQuery>; @@ -303,10 +313,14 @@ mod pallet { { /// A juror has been added to the court. JurorJoined { juror: T::AccountId, stake: BalanceOf }, - /// A juror prepared to exit the court. - JurorPreparedExit { juror: T::AccountId }, - /// A juror has been removed from the court. - JurorExited { juror: T::AccountId, exit_amount: BalanceOf, active_lock: BalanceOf }, + /// A court participant prepared to exit the court. + ExitPrepared { court_participant: T::AccountId }, + /// A court participant has been removed from the court. + ExitedCourt { + court_participant: T::AccountId, + exit_amount: BalanceOf, + active_lock: BalanceOf, + }, /// A juror has voted in a court. JurorVoted { court_id: CourtId, juror: T::AccountId, commitment: T::Hash }, /// A juror has revealed their vote. @@ -325,20 +339,20 @@ mod pallet { salt: T::Hash, }, /// A delegator has delegated their stake to jurors. - DelegatedToJurors { + DelegatorJoined { delegator: T::AccountId, - amount: BalanceOf, + stake: BalanceOf, delegated_jurors: Vec, }, /// A market has been appealed. CourtAppealed { court_id: CourtId, appeal_number: u32 }, - /// A new token amount was minted for the court jurors. - MintedInCourt { juror: T::AccountId, amount: BalanceOf }, - /// The juror stakes have been reassigned. The losing jurors have been slashed. + /// A new token amount was minted for a court participant. + MintedInCourt { court_participant: T::AccountId, amount: BalanceOf }, + /// The juror and delegator stakes have been reassigned. The losing jurors have been slashed. /// The winning jurors have been rewarded by the losers. /// The losing jurors are those, who did not vote, /// were denounced or did not reveal their vote. - JurorStakesReassigned { court_id: CourtId }, + StakesReassigned { court_id: CourtId }, /// The yearly inflation rate has been set. InflationSet { inflation: Perbill }, } @@ -372,12 +386,12 @@ mod pallet { /// The amount is below the minimum required stake. BelowMinJurorStake, /// The maximum number of possible jurors has been reached. - MaxJurorsReached, + MaxCourtParticipantsReached, /// In order to exit the court the juror has to exit /// the pool first with `prepare_exit_court`. - JurorAlreadyPreparedExit, - /// The juror needs to exit the court and then rejoin. - JurorNeedsToExit, + AlreadyPreparedExit, + /// The court participant needs to exit the court and then rejoin. + NeedToExit, /// The juror was not randomly selected for the court. JurorNotDrawn, /// The juror was drawn but did not manage to commitmently vote within the court. @@ -391,7 +405,7 @@ mod pallet { /// The juror stakes of the court already got reassigned. CourtAlreadyReassigned, /// There are not enough jurors in the pool. - NotEnoughJurorsStake, + NotEnoughJurorsAndDelegatorsStake, /// The report of the market was not found. MarketReportNotFound, /// The maximum number of court ids is reached. @@ -405,7 +419,7 @@ mod pallet { /// The amount is too low to kick the lowest juror out of the stake-weighted pool. AmountBelowLowestJuror, /// This should not happen, because the juror account should only be once in a pool. - JurorTwiceInPool, + CourtParticipantTwiceInPool, /// The caller of this function is not part of the juror draws. CallerNotInSelectedDraws, /// The callers balance is lower than the appeal bond. @@ -466,6 +480,14 @@ mod pallet { impl Pallet { /// Join to become a juror, who is able to get randomly selected /// for court cases according to the provided stake. + /// If the juror gets selected for a court case, the juror has to vote and reveal the vote. + /// If the juror does not vote or reveal the vote, the juror gets slashed + /// by the selected multiple of `MinJurorStake` for the court. + /// The risked amount depends on the juror random selection algorithm, + /// but is at most (`MaxSelectedDraws` / 2) mulitplied by the `MinJurorStake` + /// for all jurors and delegators in one court. + /// Assume you get randomly selected on one of these `MinJurorStake`'s. + /// Then you risk at most `MinJurorStake` for this court. /// The probability to get selected is higher the more funds are staked. /// The `amount` of this call represents the total stake of the juror. /// If the pool is full, the lowest staked juror is removed from the juror pool. @@ -477,7 +499,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(T::WeightInfo::join_court(T::MaxJurors::get()))] + #[pallet::weight(T::WeightInfo::join_court(T::MaxCourtParticipants::get()))] #[transactional] pub fn join_court( origin: OriginFor, @@ -506,7 +528,7 @@ mod pallet { /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(T::WeightInfo::delegate(T::MaxJurors::get(), delegations.len() as u32))] + #[pallet::weight(T::WeightInfo::delegate(T::MaxCourtParticipants::get(), delegations.len() as u32))] #[transactional] pub fn delegate( origin: OriginFor, @@ -520,10 +542,10 @@ mod pallet { let mut sorted_delegations: DelegationsOf = delegations.clone().try_into().map_err(|_| Error::::MaxDelegationsReached)?; - let jurors = JurorPool::::get(); + let pool = CourtPool::::get(); let is_valid_set = sorted_delegations.iter().all(|pretended_juror| { - >::get(pretended_juror).map_or(false, |pretended_juror_info| { - Self::get_pool_item(&jurors, pretended_juror_info.stake, pretended_juror) + >::get(pretended_juror).map_or(false, |pretended_juror_info| { + Self::get_pool_item(&pool, pretended_juror_info.stake, pretended_juror) .is_some() && pretended_juror_info.delegations.is_none() }) @@ -538,64 +560,62 @@ mod pallet { ensure!(!has_duplicates, Error::::IdenticalDelegationsNotAllowed); ensure!(!sorted_delegations.contains(&who), Error::::SelfDelegationNotAllowed); - let jurors_len = Self::do_join_court(&who, amount, Some(sorted_delegations))?; + let pool_len = Self::do_join_court(&who, amount, Some(sorted_delegations))?; - Self::deposit_event(Event::DelegatedToJurors { + Self::deposit_event(Event::DelegatorJoined { delegator: who, - amount, + stake: amount, delegated_jurors: delegations, }); - Ok(Some(T::WeightInfo::delegate(jurors_len, delegations_len)).into()) + Ok(Some(T::WeightInfo::delegate(pool_len, delegations_len)).into()) } - /// Prepare as a juror to exit the court. - /// When this is called the juror is not anymore able to get drawn for new cases. - /// The juror gets removed from the stake-weighted pool. - /// After that the juror can exit the court. + /// Prepare as a court participant (juror or delegator) to exit the court. + /// When this is called the court participant is not anymore able to get drawn for new cases. + /// The court participant gets removed from the stake-weighted pool. + /// After that the court participant can exit the court. /// /// # Weight /// /// Complexity: `O(log(n))`, where `n` is the number of jurors in the stake-weighted pool. - #[pallet::weight(T::WeightInfo::prepare_exit_court(T::MaxJurors::get()))] + #[pallet::weight(T::WeightInfo::prepare_exit_court(T::MaxCourtParticipants::get()))] #[transactional] pub fn prepare_exit_court(origin: OriginFor) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let mut prev_juror_info = - >::get(&who).ok_or(Error::::JurorDoesNotExist)?; - ensure!( - prev_juror_info.prepare_exit_at.is_none(), - Error::::JurorAlreadyPreparedExit - ); + let mut prev_p_info = + >::get(&who).ok_or(Error::::JurorDoesNotExist)?; + ensure!(prev_p_info.prepare_exit_at.is_none(), Error::::AlreadyPreparedExit); - let mut jurors = JurorPool::::get(); - let jurors_len = jurors.len() as u32; + let mut pool = CourtPool::::get(); + let pool_len = pool.len() as u32; // do not error in the else case // because the juror might have been already removed from the pool - if let Some((index, _)) = Self::get_pool_item(&jurors, prev_juror_info.stake, &who) { - jurors.remove(index); - >::put(jurors); + if let Some((index, _)) = Self::get_pool_item(&pool, prev_p_info.stake, &who) { + pool.remove(index); + >::put(pool); } let now = >::block_number(); - prev_juror_info.prepare_exit_at = Some(now); - >::insert(&who, prev_juror_info); + prev_p_info.prepare_exit_at = Some(now); + >::insert(&who, prev_p_info); - Self::deposit_event(Event::JurorPreparedExit { juror: who }); + Self::deposit_event(Event::ExitPrepared { court_participant: who }); - Ok(Some(T::WeightInfo::prepare_exit_court(jurors_len)).into()) + Ok(Some(T::WeightInfo::prepare_exit_court(pool_len)).into()) } /// Exit the court. /// The stake which is not locked by any court case is unlocked. /// `prepare_exit_court` must be called before - /// to remove the juror from the stake-weighted pool. + /// to remove the court participant (juror or delegator) from the stake-weighted pool. /// /// # Arguments /// - /// - `juror`: The juror, who is assumed to be not be part of the pool anymore. + /// - `court_participant`: The court participant, + /// who is assumed to be not be part of the pool anymore. /// /// # Weight /// @@ -604,44 +624,48 @@ mod pallet { #[transactional] pub fn exit_court( origin: OriginFor, - juror: AccountIdLookupOf, + court_participant: AccountIdLookupOf, ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - let juror = T::Lookup::lookup(juror)?; + let who = T::Lookup::lookup(court_participant)?; - let mut prev_juror_info = - >::get(&juror).ok_or(Error::::JurorDoesNotExist)?; + let mut prev_p_info = + >::get(&who).ok_or(Error::::JurorDoesNotExist)?; let now = >::block_number(); let prepare_exit_at = - prev_juror_info.prepare_exit_at.ok_or(Error::::PrepareExitAtNotPresent)?; + prev_p_info.prepare_exit_at.ok_or(Error::::PrepareExitAtNotPresent)?; ensure!( now.saturating_sub(prepare_exit_at) >= T::InflationPeriod::get(), Error::::WaitFullInflationPeriod ); - let (exit_amount, active_lock, weight) = if prev_juror_info.active_lock.is_zero() { - T::Currency::remove_lock(T::CourtLockId::get(), &juror); - Jurors::::remove(&juror); - (prev_juror_info.stake, >::zero(), T::WeightInfo::exit_court_remove()) + let (exit_amount, active_lock, weight) = if prev_p_info.active_lock.is_zero() { + T::Currency::remove_lock(T::CourtLockId::get(), &who); + Participants::::remove(&who); + (prev_p_info.stake, >::zero(), T::WeightInfo::exit_court_remove()) } else { - let active_lock = prev_juror_info.active_lock; - let exit_amount = prev_juror_info.stake.saturating_sub(active_lock); + let active_lock = prev_p_info.active_lock; + let exit_amount = prev_p_info.stake.saturating_sub(active_lock); T::Currency::set_lock( T::CourtLockId::get(), - &juror, + &who, active_lock, WithdrawReasons::all(), ); - prev_juror_info.stake = active_lock; - Jurors::::insert(&juror, prev_juror_info); + prev_p_info.stake = active_lock; + Participants::::insert(&who, prev_p_info); (exit_amount, active_lock, T::WeightInfo::exit_court_set()) }; - Self::deposit_event(Event::JurorExited { juror, exit_amount, active_lock }); + Self::deposit_event(Event::ExitedCourt { + court_participant: who, + exit_amount, + active_lock, + }); Ok(Some(weight).into()) } @@ -675,7 +699,7 @@ mod pallet { let mut draws = >::get(court_id); - match draws.binary_search_by_key(&who, |draw| draw.juror.clone()) { + match draws.binary_search_by_key(&who, |draw| draw.court_participant.clone()) { Ok(index) => { let draw = draws[index].clone(); @@ -707,7 +731,8 @@ mod pallet { /// Denounce a juror during the voting period for which the commitment vote is known. /// This is useful to punish the behaviour that jurors reveal /// their commitments before the voting period ends. - /// A check of `commitment_hash == hash(juror ++ vote_item ++ salt)` is performed for validation. + /// A check of `commitment_hash == hash(juror ++ vote_item ++ salt)` + /// is performed for validation. /// /// # Arguments /// @@ -719,8 +744,7 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n)`, where `n` is the number of jurors - /// in the list of random selections (draws). + /// Complexity: `O(n)`, where `n` is the number of selected draws. #[pallet::weight(T::WeightInfo::denounce_vote(T::MaxSelectedDraws::get()))] #[transactional] pub fn denounce_vote( @@ -740,7 +764,7 @@ mod pallet { ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); - ensure!(>::contains_key(&juror), Error::::JurorDoesNotExist); + ensure!(>::contains_key(&juror), Error::::JurorDoesNotExist); let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; Self::check_vote_item(&court, &vote_item)?; @@ -753,7 +777,7 @@ mod pallet { ); let mut draws = >::get(court_id); - match draws.binary_search_by_key(&juror, |draw| draw.juror.clone()) { + match draws.binary_search_by_key(&juror, |draw| draw.court_participant.clone()) { Ok(index) => { let draw = draws[index].clone(); @@ -762,7 +786,7 @@ mod pallet { let commitment = Self::get_hashed_commitment(draw.vote, raw_commmitment)?; - // slash for the misbehaviour happens in reassign_juror_stakes + // slash for the misbehaviour happens in reassign_court_stakes let raw_vote = Vote::Denounced { commitment, vote_item: vote_item.clone(), salt }; draws[index] = Draw { vote: raw_vote, ..draw }; @@ -796,8 +820,7 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n)`, where `n` is the number of jurors - /// in the list of random selections (draws). + /// Complexity: `O(n)`, where `n` is the number of selected draws. #[pallet::weight(T::WeightInfo::reveal_vote(T::MaxSelectedDraws::get()))] #[transactional] pub fn reveal_vote( @@ -812,7 +835,7 @@ mod pallet { T::VoteCheck::pre_validate(&market_id, vote_item.clone())?; } - ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); + ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; Self::check_vote_item(&court, &vote_item)?; @@ -823,7 +846,7 @@ mod pallet { ); let mut draws = >::get(court_id); - match draws.binary_search_by_key(&who, |draw| draw.juror.clone()) { + match draws.binary_search_by_key(&who, |draw| draw.court_participant.clone()) { Ok(index) => { let draw = draws[index].clone(); @@ -834,7 +857,7 @@ mod pallet { let raw_vote = Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }; - draws[index] = Draw { juror: who.clone(), vote: raw_vote, ..draw }; + draws[index] = Draw { court_participant: who.clone(), vote: raw_vote, ..draw }; } Err(_) => return Err(Error::::JurorNotDrawn.into()), } @@ -848,8 +871,12 @@ mod pallet { Ok(Some(T::WeightInfo::reveal_vote(draws_len)).into()) } - /// Trigger an appeal for a court. The last appeal does not trigger a new court round + /// Initiate an appeal for a court + /// if the presumptive winner of the last vote round is believed to be incorrect. + /// The last appeal does not trigger a new court round /// but instead it marks the court mechanism for this market as failed. + /// If the court failed, the prediction markets pallet takes over the dispute resolution. + /// The prediction markets pallet might allow to trigger a global token holder vote. /// /// # Arguments /// @@ -857,10 +884,9 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n)`, where `n` is the number of jurors. - /// It depends heavily on `choose_multiple_weighted` of `select_jurors`. + /// Complexity: It depends heavily on the complexity of `select_jurors`. #[pallet::weight(T::WeightInfo::appeal( - T::MaxJurors::get(), + T::MaxCourtParticipants::get(), T::MaxAppeals::get(), CacheSize::get(), CacheSize::get(), @@ -893,7 +919,7 @@ mod pallet { let last_resolve_at = court.cycle_ends.appeal; // used for benchmarking, juror pool is queried inside `select_jurors` - let jurors_len = >::decode_len().unwrap_or(0) as u32; + let pool_len = >::decode_len().unwrap_or(0) as u32; let mut ids_len_1 = 0u32; // if appeal_number == MaxAppeals, then don't start a new appeal round @@ -915,7 +941,7 @@ mod pallet { ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; } >::insert(court_id, new_draws); - Self::unlock_jurors_from_last_draw(court_id, old_draws); + Self::unlock_participants_from_last_draw(court_id, old_draws); } let mut ids_len_0 = 0u32; @@ -930,12 +956,15 @@ mod pallet { let appeal_number = appeal_number as u32; Self::deposit_event(Event::CourtAppealed { court_id, appeal_number }); - Ok(Some(T::WeightInfo::appeal(jurors_len, appeal_number, ids_len_0, ids_len_1)).into()) + Ok(Some(T::WeightInfo::appeal(pool_len, appeal_number, ids_len_0, ids_len_1)).into()) } - /// The juror stakes get reassigned according to the plurality decision of the jurors. - /// The losing jurors get slashed and pay for the winning jurors. - /// The tardy or denounced jurors get slashed. + /// Reassign the stakes of the jurors and delegators + /// for the selected draws of the specified court. + /// The losing jurors and delegators get slashed and + /// pay for the winning jurors and delegators. + /// The tardy (juror did not reveal or did not vote) or denounced jurors + /// and associated delegators get slashed and reward the winners. /// /// # Arguments /// @@ -943,10 +972,11 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n)`, where `n` is the number of randomly selected jurors for this court. - #[pallet::weight(T::WeightInfo::reassign_juror_stakes(T::MaxSelectedDraws::get()))] + /// Complexity: It depends heavily on the number of delegations + /// and the number of selected draws. + #[pallet::weight(T::WeightInfo::reassign_court_stakes(T::MaxSelectedDraws::get()))] #[transactional] - pub fn reassign_juror_stakes( + pub fn reassign_court_stakes( origin: OriginFor, court_id: CourtId, ) -> DispatchResultWithPostInfo { @@ -981,14 +1011,14 @@ mod pallet { Vote::Drawn | Vote::Secret { commitment: _ } | Vote::Denounced { commitment: _, vote_item: _, salt: _ } => { - slash_juror(&draw.juror, draw.slashable); + slash_juror(&draw.court_participant, draw.slashable); } Vote::Revealed { commitment: _, vote_item, salt: _ } => { - jurors_to_stakes.entry(draw.juror).or_default().self_info = + jurors_to_stakes.entry(draw.court_participant).or_default().self_info = Some(SelfInfo { slashable: draw.slashable, vote_item }); } Vote::Delegated { delegated_stakes } => { - let delegator = draw.juror; + let delegator = draw.court_participant; for (j, delegated_stake) in delegated_stakes { // fill the delegations for each juror // [(juror_0, [(delegator_0, delegator_stake_0), ...]), @@ -1021,14 +1051,14 @@ mod pallet { }; for draw in draws { - if let Some(mut juror_info) = >::get(&draw.juror) { - juror_info.active_lock = juror_info.active_lock.saturating_sub(draw.slashable); - >::insert(&draw.juror, juror_info); + if let Some(mut p_info) = >::get(&draw.court_participant) { + p_info.active_lock = p_info.active_lock.saturating_sub(draw.slashable); + >::insert(&draw.court_participant, p_info); } else { log::warn!( - "Juror {:?} not found in Jurors storage (reassign_juror_stakes). Court id \ - {:?}.", - draw.juror, + "Participant {:?} not found in Participants storage \ + (reassign_court_stakes). Court id {:?}.", + draw.court_participant, court_id ); debug_assert!(false); @@ -1044,9 +1074,9 @@ mod pallet { >::remove(court_id); - Self::deposit_event(Event::JurorStakesReassigned { court_id }); + Self::deposit_event(Event::StakesReassigned { court_id }); - Ok(Some(T::WeightInfo::reassign_juror_stakes(draws_len)).into()) + Ok(Some(T::WeightInfo::reassign_court_stakes(draws_len)).into()) } /// Set the yearly inflation rate of the court system. @@ -1092,27 +1122,27 @@ mod pallet { inflation_period.saturated_into::().saturated_into::>(), ); - let jurors = >::get(); - let jurors_len = jurors.len() as u32; - let total_stake = jurors.iter().fold(0u128, |acc, pool_item| { + let pool = >::get(); + let pool_len = pool.len() as u32; + let total_stake = pool.iter().fold(0u128, |acc, pool_item| { acc.saturating_add(pool_item.stake.saturated_into::()) }); - for JurorPoolItem { stake, juror, .. } in jurors { + for CourtPoolItem { stake, court_participant, .. } in pool { let share = Perquintill::from_rational(stake.saturated_into::(), total_stake); let mint = share * inflation_period_mint.saturated_into::(); if let Ok(imb) = T::Currency::deposit_into_existing( - &juror, + &court_participant, mint.saturated_into::>(), ) { Self::deposit_event(Event::MintedInCourt { - juror: juror.clone(), + court_participant: court_participant.clone(), amount: imb.peek(), }); } } - return T::WeightInfo::handle_inflation(jurors_len); + return T::WeightInfo::handle_inflation(pool_len); } Weight::zero() @@ -1166,13 +1196,13 @@ mod pallet { } // Adds active lock amount. - // The added active lock amount is noted in the Jurors map. - fn add_active_lock(juror: &T::AccountId, lock_added: BalanceOf) { - if let Some(mut juror_info) = >::get(juror) { - juror_info.active_lock = juror_info.active_lock.saturating_add(lock_added); - >::insert(juror, juror_info); + // The added active lock amount is noted in the Participants map. + fn add_active_lock(court_participant: &T::AccountId, lock_added: BalanceOf) { + if let Some(mut p_info) = >::get(court_participant) { + p_info.active_lock = p_info.active_lock.saturating_add(lock_added); + >::insert(court_participant, p_info); } else { - debug_assert!(false, "Juror should exist in the Jurors map"); + debug_assert!(false, "Participant should exist in the Participants map"); } } @@ -1203,17 +1233,17 @@ mod pallet { } // Updates the `selections` map for the juror and the lock amount. - // If `juror` does not already exist in `selections`, + // If `court_participant` does not already exist in `selections`, // the vote weight is set to 1 and the lock amount is initially set. // For each call on the same juror, the vote weight is incremented by one // and the lock amount is added to the previous amount. fn update_selections( selections: &mut BTreeMap>, - juror: &T::AccountId, + court_participant: &T::AccountId, sel_add: SelectionAdd, BalanceOf>, ) { if let Some(SelectionValue { weight, slashable, delegated_stakes }) = - selections.get_mut(juror) + selections.get_mut(court_participant) { match sel_add { SelectionAdd::SelfStake { lock } => { @@ -1236,7 +1266,7 @@ mod pallet { match sel_add { SelectionAdd::SelfStake { lock } => { selections.insert( - juror.clone(), + court_participant.clone(), SelectionValue { weight: 1, slashable: lock, @@ -1251,13 +1281,13 @@ mod pallet { lock, ); selections.insert( - juror.clone(), + court_participant.clone(), SelectionValue { weight: 0, slashable: lock, delegated_stakes }, ); } SelectionAdd::DelegationWeight => { selections.insert( - juror.clone(), + court_participant.clone(), SelectionValue { weight: 1, slashable: >::zero(), @@ -1275,7 +1305,7 @@ mod pallet { delegations: &[T::AccountId], random_number: u128, ) -> Option { - let jurors: JurorPoolOf = JurorPool::::get(); + let pool: CourtPoolOf = CourtPool::::get(); let mut delegated_juror = None; for count in 0..delegations.len() { @@ -1291,13 +1321,12 @@ mod pallet { }; if let Some(del_j) = &delegated_juror { - if let Some(delegated_juror_info) = >::get(del_j) { + if let Some(delegated_juror_info) = >::get(del_j) { if delegated_juror_info.delegations.is_some() { // skip if delegated juror is delegator herself continue; } - if Self::get_pool_item(&jurors, delegated_juror_info.stake, del_j).is_some() - { + if Self::get_pool_item(&pool, delegated_juror_info.stake, del_j).is_some() { delegated_juror = Some(del_j.clone()); break; } @@ -1308,15 +1337,15 @@ mod pallet { delegated_juror } - /// Add a juror with her stake to the `selections` map. + /// Add a juror or delegator with the provided `lock_added` to the `selections` map. fn add_to_selections( selections: &mut BTreeMap>, - juror: &T::AccountId, + court_participant: &T::AccountId, lock_added: BalanceOf, random_number: u128, ) -> Result<(), SelectionError> { - let delegations_opt = - >::get(juror.clone()).and_then(|juror_info| juror_info.delegations); + let delegations_opt = >::get(court_participant.clone()) + .and_then(|p_info| p_info.delegations); match delegations_opt { Some(delegations) => { let delegated_juror = @@ -1332,26 +1361,27 @@ mod pallet { lock: lock_added, }; // delegator risks his stake (to delegated juror), but gets no vote weight - Self::update_selections(selections, juror, sel_add); + Self::update_selections(selections, court_participant, sel_add); } None => { let sel_add = SelectionAdd::SelfStake { lock: lock_added }; - Self::update_selections(selections, juror, sel_add); + Self::update_selections(selections, court_participant, sel_add); } } Ok(()) } - // Match the random numbers to select some jurors from the pool. + // Match the random numbers to select some jurors and delegators from the pool. // The active lock (and consumed stake) of the selected jurors // is increased by the random selection weight. + // If a delegator is chosen by a random number, one delegated juror gets the vote weight. fn get_selections( - jurors: &mut JurorPoolOf, + pool: &mut CourtPoolOf, random_section_starts: BTreeSet, cumulative_section_starts: Vec<(u128, bool)>, ) -> BTreeMap> { - debug_assert!(jurors.len() == cumulative_section_starts.len()); + debug_assert!(pool.len() == cumulative_section_starts.len()); debug_assert!({ let prev = cumulative_section_starts.clone(); let mut sorted = cumulative_section_starts.clone(); @@ -1373,13 +1403,13 @@ mod pallet { let range_index = cumulative_section_starts .binary_search(&(random_section_start, allow_zero_stake)) .unwrap_or_else(|i| i); - if let Some(pool_item) = jurors.get_mut(range_index) { + if let Some(pool_item) = pool.get_mut(range_index) { let unconsumed = pool_item.stake.saturating_sub(pool_item.consumed_stake); let lock_added = unconsumed.min(T::MinJurorStake::get()); match Self::add_to_selections( &mut selections, - &pool_item.juror, + &pool_item.court_participant, lock_added, random_section_start, ) { @@ -1393,7 +1423,7 @@ mod pallet { } } - Self::add_active_lock(&pool_item.juror, lock_added); + Self::add_active_lock(&pool_item.court_participant, lock_added); pool_item.consumed_stake = pool_item.consumed_stake.saturating_add(lock_added); } else { debug_assert!(false, "Each range index should match to a juror."); @@ -1401,7 +1431,7 @@ mod pallet { } for i in invalid_juror_indices { - jurors.remove(i); + pool.remove(i); } selections @@ -1413,28 +1443,33 @@ mod pallet { ) -> Vec> { selections .into_iter() - .map(|(juror, SelectionValue { weight, slashable, delegated_stakes })| Draw { - juror, - weight, - vote: if !delegated_stakes.is_empty() { - debug_assert!( - weight.is_zero(), - "Jurors who delegated shouldn't have voting weight." - ); - debug_assert!( - delegated_stakes - .clone() - .into_iter() - .fold(Zero::zero(), |acc: BalanceOf, (_, stake)| acc - .saturating_add(stake)) - == slashable - ); - Vote::Delegated { delegated_stakes } - } else { - Vote::Drawn + .map( + |( + court_participant, + SelectionValue { weight, slashable, delegated_stakes }, + )| Draw { + court_participant, + weight, + vote: if !delegated_stakes.is_empty() { + debug_assert!( + weight.is_zero(), + "Delegators shouldn't have voting weight." + ); + debug_assert!( + delegated_stakes + .clone() + .into_iter() + .fold(Zero::zero(), |acc: BalanceOf, (_, stake)| acc + .saturating_add(stake)) + == slashable + ); + Vote::Delegated { delegated_stakes } + } else { + Vote::Drawn + }, + slashable, }, - slashable, - }) + ) .collect() } @@ -1446,14 +1481,14 @@ mod pallet { pub(crate) fn choose_multiple_weighted( draw_weight: usize, ) -> Result>, DispatchError> { - let mut jurors = >::get(); + let mut pool = >::get(); let min_juror_stake = T::MinJurorStake::get().saturated_into::(); let mut total_unconsumed = 0u128; let mut cumulative_section_starts = Vec::new(); let mut running_total = 0u128; - for pool_item in &jurors { + for pool_item in &pool { let unconsumed = pool_item .stake .saturating_sub(pool_item.consumed_stake) @@ -1475,30 +1510,32 @@ mod pallet { ); let required_stake = (draw_weight as u128).saturating_mul(min_juror_stake); - ensure!(total_unconsumed >= required_stake, Error::::NotEnoughJurorsStake); + ensure!( + total_unconsumed >= required_stake, + Error::::NotEnoughJurorsAndDelegatorsStake + ); let random_section_starts = Self::get_n_random_section_starts(draw_weight, total_unconsumed)?; let selections = - Self::get_selections(&mut jurors, random_section_starts, cumulative_section_starts); - >::put(jurors); + Self::get_selections(&mut pool, random_section_starts, cumulative_section_starts); + >::put(pool); Ok(Self::convert_selections_to_draws(selections)) } - // Reduce the active lock of the jurors from the last draw. + // Reduce the active lock of the jurors from the last draws. // This is useful so that the jurors can thaw their non-locked stake. - fn unlock_jurors_from_last_draw(court_id: CourtId, last_draws: SelectedDrawsOf) { - // keep in mind that the old draw likely contains different jurors + fn unlock_participants_from_last_draw(court_id: CourtId, last_draws: SelectedDrawsOf) { + // keep in mind that the old draw likely contains different jurors and delegators for old_draw in last_draws { - if let Some(mut juror_info) = >::get(&old_draw.juror) { - juror_info.active_lock = - juror_info.active_lock.saturating_sub(old_draw.slashable); - >::insert(&old_draw.juror, juror_info); + if let Some(mut p_info) = >::get(&old_draw.court_participant) { + p_info.active_lock = p_info.active_lock.saturating_sub(old_draw.slashable); + >::insert(&old_draw.court_participant, p_info); } else { log::warn!( - "Juror {:?} not found in Jurors storage (unlock_jurors_from_last_draw). \ - Court id {:?}.", - old_draw.juror, + "Participant {:?} not found in Participants storage \ + (unlock_participants_from_last_draw). Court id {:?}.", + old_draw.court_participant, court_id ); debug_assert!(false); @@ -1506,9 +1543,9 @@ mod pallet { } } - // Selects the jurors for the next round. - // The `consumed_stake` in `JurorPool` and `active_lock` in `Jurors` is increased - // equally according to the weight inside the `new_draws`. + // Selects the jurors and delegators for the next court round. + // The `consumed_stake` in `CourtPool` and `active_lock` in `Participants` is increased + // equally according to the draw weight. // With increasing `consumed_stake` the probability to get selected // in further court rounds shrinks. // @@ -1528,7 +1565,7 @@ mod pallet { // this is helpful to use binary search later on let prev = random_jurors.clone(); let mut sorted = random_jurors.clone(); - sorted.sort_by_key(|draw| draw.juror.clone()); + sorted.sort_by_key(|draw| draw.court_participant.clone()); prev.len() == sorted.len() && prev.iter().zip(sorted.iter()).all(|(a, b)| a == b) }); @@ -1542,8 +1579,8 @@ mod pallet { // ensure that we don't truncate some of the selections debug_assert!( random_jurors.len() <= T::MaxSelectedDraws::get() as usize, - "The number of randomly selected jurors should be less than or equal to \ - `MaxSelectedDraws`." + "The number of randomly selected jurors and delegators should be less than or \ + equal to `MaxSelectedDraws`." ); Ok(>::truncate_from(random_jurors)) } @@ -1551,17 +1588,17 @@ mod pallet { // Returns (index, pool_item) if the pool item is part of the juror pool. // It returns None otherwise. pub(crate) fn get_pool_item<'a>( - jurors: &'a [JurorPoolItemOf], + pool: &'a [CourtPoolItemOf], stake: BalanceOf, - juror: &T::AccountId, - ) -> Option<(usize, &'a JurorPoolItemOf)> { - if let Ok(i) = jurors.binary_search_by_key(&(stake, juror), |pool_item| { - (pool_item.stake, &pool_item.juror) + court_participant: &T::AccountId, + ) -> Option<(usize, &'a CourtPoolItemOf)> { + if let Ok(i) = pool.binary_search_by_key(&(stake, court_participant), |pool_item| { + (pool_item.stake, &pool_item.court_participant) }) { - return Some((i, &jurors[i])); + return Some((i, &pool[i])); } - // this None case can happen whenever the juror decided to leave the court - // or was kicked out of the juror pool because of the lowest stake + // this None case can happen whenever the court participant decided to leave the court + // or was kicked out of the court pool because of the lowest stake None } @@ -1635,9 +1672,9 @@ mod pallet { // Returns a cryptographically secure random number generator // implementation based on the seed provided by the `Config::Random` type - // and the `JurorsSelectionNonce` storage. + // and the `SelectionNonce` storage. pub(crate) fn rng() -> impl RngCore { - let nonce = >::mutate(|n| { + let nonce = >::mutate(|n| { let rslt = *n; *n = n.wrapping_add(1); rslt @@ -1862,7 +1899,7 @@ mod pallet { T: Config, { fn on_dispute_max_weight() -> Weight { - T::WeightInfo::on_dispute(T::MaxJurors::get(), CacheSize::get()) + T::WeightInfo::on_dispute(T::MaxCourtParticipants::get(), CacheSize::get()) } fn on_resolution_max_weight() -> Weight { @@ -1916,7 +1953,7 @@ mod pallet { court_id.checked_add(One::one()).ok_or(Error::::MaxCourtIdReached)?; let appeal_number = 0usize; - let jurors_len = >::decode_len().unwrap_or(0) as u32; + let pool_len = >::decode_len().unwrap_or(0) as u32; let new_draws = Self::select_jurors(appeal_number)?; let now = >::block_number(); @@ -1944,7 +1981,7 @@ mod pallet { let res = ResultWithWeightInfo { result: (), - weight: T::WeightInfo::on_dispute(jurors_len, ids_len), + weight: T::WeightInfo::on_dispute(pool_len, ids_len), }; Ok(res) @@ -1965,7 +2002,7 @@ mod pallet { let draws = SelectedDraws::::get(court_id); let draws_len = draws.len() as u32; let winner_vote_item = Self::get_latest_winner_vote_item(court_id, draws.as_slice())?; - Self::unlock_jurors_from_last_draw(court_id, draws); + Self::unlock_participants_from_last_draw(court_id, draws); court.status = CourtStatus::Closed { winner: winner_vote_item.clone() }; >::insert(court_id, court); @@ -2052,12 +2089,15 @@ mod pallet { let court_id = >::get(market_id) .ok_or(Error::::MarketIdToCourtIdNotFound)?; - let jurors = JurorPool::::get(); - let jurors_unconsumed_stake = jurors.iter().fold(0u128, |acc, pool_item| { + let pool = CourtPool::::get(); + let min_juror_stake = T::MinJurorStake::get().saturated_into::(); + let pool_unconsumed_stake = pool.iter().fold(0u128, |acc, pool_item| { let unconsumed = pool_item .stake .saturating_sub(pool_item.consumed_stake) .saturated_into::(); + let remainder = unconsumed % min_juror_stake; + let unconsumed = unconsumed.saturating_sub(remainder); acc.saturating_add(unconsumed) }); @@ -2071,7 +2111,7 @@ mod pallet { let valid_period = Self::check_appealable_market(court_id, &court, now).is_ok(); if appeals.is_full() - || (valid_period && (jurors_unconsumed_stake < required_stake)) + || (valid_period && (pool_unconsumed_stake < required_stake)) { has_failed = true; } @@ -2087,7 +2127,7 @@ mod pallet { let necessary_draws_weight = Self::necessary_draws_weight(0usize); let required_stake = (necessary_draws_weight as u128) .saturating_mul(T::MinJurorStake::get().saturated_into::()); - if during_dispute_duration && jurors_unconsumed_stake < required_stake { + if during_dispute_duration && pool_unconsumed_stake < required_stake { has_failed = true; } } @@ -2141,7 +2181,7 @@ mod pallet { let old_draws = SelectedDraws::::get(court_id); let draws_len = old_draws.len() as u32; - Self::unlock_jurors_from_last_draw(court_id, old_draws); + Self::unlock_participants_from_last_draw(court_id, old_draws); >::remove(court_id); >::remove(court_id); @@ -2167,7 +2207,7 @@ mod pallet { let old_draws = SelectedDraws::::get(court_id); let draws_len = old_draws.len() as u32; - Self::unlock_jurors_from_last_draw(court_id, old_draws); + Self::unlock_participants_from_last_draw(court_id, old_draws); >::remove(court_id); >::remove(court_id); @@ -2200,72 +2240,82 @@ impl Pallet { let free_balance = T::Currency::free_balance(who); ensure!(amount <= free_balance, Error::::AmountExceedsBalance); - let mut jurors = JurorPool::::get(); + let mut pool = CourtPool::::get(); - let (active_lock, consumed_stake) = if let Some(prev_juror_info) = >::get(who) { - ensure!(amount > prev_juror_info.stake, Error::::AmountBelowLastJoin); - let (index, pool_item) = Self::get_pool_item(&jurors, prev_juror_info.stake, who) - .ok_or(Error::::JurorNeedsToExit)?; + let (active_lock, consumed_stake) = if let Some(prev_p_info) = >::get(who) { + ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); + let (index, pool_item) = + Self::get_pool_item(&pool, prev_p_info.stake, who).ok_or(Error::::NeedToExit)?; debug_assert!( - prev_juror_info.prepare_exit_at.is_none(), + prev_p_info.prepare_exit_at.is_none(), "If the pool item is found, the prepare_exit_at could have never been written." ); let consumed_stake = pool_item.consumed_stake; - jurors.remove(index); - (prev_juror_info.active_lock, consumed_stake) + pool.remove(index); + (prev_p_info.active_lock, consumed_stake) } else { - if jurors.is_full() { - let lowest_item = jurors.first(); + if pool.is_full() { + let lowest_item = pool.first(); let lowest_stake = lowest_item .map(|pool_item| pool_item.stake) .unwrap_or_else(>::zero); debug_assert!({ - let mut sorted = jurors.clone(); - sorted.sort_by_key(|pool_item| (pool_item.stake, pool_item.juror.clone())); - jurors.len() == sorted.len() - && jurors + let mut sorted = pool.clone(); + sorted.sort_by_key(|pool_item| { + (pool_item.stake, pool_item.court_participant.clone()) + }); + pool.len() == sorted.len() + && pool .iter() .zip(sorted.iter()) .all(|(a, b)| lowest_stake <= a.stake && a == b) }); ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); - // remove the lowest staked juror - jurors.remove(0); + // remove the lowest staked court participant + pool.remove(0); } (>::zero(), >::zero()) }; - match jurors - .binary_search_by_key(&(amount, who), |pool_item| (pool_item.stake, &pool_item.juror)) - { + match pool.binary_search_by_key(&(amount, who), |pool_item| { + (pool_item.stake, &pool_item.court_participant) + }) { Ok(_) => { debug_assert!( false, - "This should never happen, because we are removing the juror above." + "This should never happen, because we are removing the court participant \ + above." ); - return Err(Error::::JurorTwiceInPool.into()); + return Err(Error::::CourtParticipantTwiceInPool.into()); } - Err(i) => jurors - .try_insert(i, JurorPoolItem { stake: amount, juror: who.clone(), consumed_stake }) + Err(i) => pool + .try_insert( + i, + CourtPoolItem { stake: amount, court_participant: who.clone(), consumed_stake }, + ) .map_err(|_| { debug_assert!( false, "This should never happen, because we are removing the lowest staked \ - juror above." + court participant above." ); - Error::::MaxJurorsReached + Error::::MaxCourtParticipantsReached })?, }; T::Currency::set_lock(T::CourtLockId::get(), who, amount, WithdrawReasons::all()); - let jurors_len = jurors.len() as u32; - JurorPool::::put(jurors); + let pool_len = pool.len() as u32; + CourtPool::::put(pool); - let juror_info = - JurorInfoOf:: { stake: amount, active_lock, prepare_exit_at: None, delegations }; - >::insert(who, juror_info); + let p_info = CourtParticipantInfoOf:: { + stake: amount, + active_lock, + prepare_exit_at: None, + delegations, + }; + >::insert(who, p_info); - Ok(jurors_len) + Ok(pool_len) } } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 72330c4ec..472247718 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -40,8 +40,8 @@ use zeitgeist_primitives::{ constants::mock::{ AppealBond, BlockHashCount, BlocksPerYear, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, InflationPeriod, MaxAppeals, MaxApprovals, - MaxDelegations, MaxJurors, MaxReserves, MaxSelectedDraws, MinJurorStake, MinimumPeriod, - PmPalletId, RequestInterval, BASE, + MaxCourtParticipants, MaxDelegations, MaxReserves, MaxSelectedDraws, MinJurorStake, + MinimumPeriod, PmPalletId, RequestInterval, BASE, }, traits::DisputeResolutionApi, types::{ @@ -197,7 +197,7 @@ impl crate::Config for Runtime { type MaxAppeals = MaxAppeals; type MaxDelegations = MaxDelegations; type MaxSelectedDraws = MaxSelectedDraws; - type MaxJurors = MaxJurors; + type MaxCourtParticipants = MaxCourtParticipants; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; type CourtPalletId = CourtPalletId; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index ecb6c1609..8c608c1ac 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -26,9 +26,9 @@ use crate::{ }, mock_storage::pallet::MarketIdsPerDisputeBlock, types::{CourtStatus, Draw, Vote, VoteItem}, - AppealInfo, BalanceOf, CourtId, CourtIdToMarketId, Courts, Error, Event, JurorInfo, - JurorInfoOf, JurorPool, JurorPoolItem, JurorPoolOf, Jurors, MarketIdToCourtId, MarketOf, - NegativeImbalanceOf, RequestBlock, SelectedDraws, + AppealInfo, BalanceOf, CourtId, CourtIdToMarketId, CourtParticipantInfo, + CourtParticipantInfoOf, CourtPool, CourtPoolItem, CourtPoolOf, Courts, Error, Event, + MarketIdToCourtId, MarketOf, NegativeImbalanceOf, Participants, RequestBlock, SelectedDraws, }; use alloc::collections::BTreeMap; use frame_support::{ @@ -46,7 +46,7 @@ use zeitgeist_primitives::{ constants::{ mock::{ AppealBond, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, - InflationPeriod, MaxAppeals, MaxJurors, MinJurorStake, RequestInterval, + InflationPeriod, MaxAppeals, MaxCourtParticipants, MinJurorStake, RequestInterval, }, BASE, }, @@ -135,11 +135,11 @@ fn put_alice_in_draw(court_id: CourtId, stake: BalanceOf) { let mut draws = >::get(court_id); assert!(!draws.is_empty()); let slashable = MinJurorStake::get(); - draws[0] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; + draws[0] = Draw { court_participant: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(court_id, draws); - >::insert( + >::insert( ALICE, - JurorInfo { + CourtParticipantInfo { stake, active_lock: slashable, prepare_exit_at: None, @@ -151,7 +151,7 @@ fn put_alice_in_draw(court_id: CourtId, stake: BalanceOf) { fn set_alice_after_vote( outcome: OutcomeReport, ) -> (CourtId, ::Hash, ::Hash) { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -181,7 +181,7 @@ fn exit_court_successfully_removes_a_juror_and_frees_balances() { assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); run_blocks(InflationPeriod::get()); assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); - assert_eq!(Jurors::::iter().count(), 0); + assert_eq!(Participants::::iter().count(), 0); assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE); assert_eq!(Balances::locks(ALICE), vec![]); }); @@ -195,10 +195,10 @@ fn join_court_successfully_stores_required_data() { assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); System::assert_last_event(Event::JurorJoined { juror: ALICE, stake: amount }.into()); assert_eq!( - Jurors::::iter().next().unwrap(), + Participants::::iter().next().unwrap(), ( ALICE, - JurorInfo { + CourtParticipantInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None, @@ -209,8 +209,8 @@ fn join_court_successfully_stores_required_data() { assert_eq!(Balances::free_balance(ALICE), alice_free_balance_before); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_eq!( - JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] + CourtPool::::get().into_inner(), + vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] ); }); } @@ -223,14 +223,15 @@ fn join_court_works_multiple_joins() { assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_eq!( - JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] + CourtPool::::get().into_inner(), + vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] ); assert_eq!( - Jurors::::iter().collect::)>>(), + Participants::::iter() + .collect::)>>(), vec![( ALICE, - JurorInfo { + CourtParticipantInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None, @@ -242,16 +243,16 @@ fn join_court_works_multiple_joins() { assert_ok!(Court::join_court(Origin::signed(BOB), amount)); assert_eq!(Balances::locks(BOB), vec![the_lock(amount)]); assert_eq!( - JurorPool::::get().into_inner(), + CourtPool::::get().into_inner(), vec![ - JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }, - JurorPoolItem { stake: amount, juror: BOB, consumed_stake: 0 } + CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }, + CourtPoolItem { stake: amount, court_participant: BOB, consumed_stake: 0 } ] ); - assert_eq!(Jurors::::iter().count(), 2); + assert_eq!(Participants::::iter().count(), 2); assert_eq!( - Jurors::::get(ALICE).unwrap(), - JurorInfo { + Participants::::get(ALICE).unwrap(), + CourtParticipantInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None, @@ -259,8 +260,8 @@ fn join_court_works_multiple_joins() { } ); assert_eq!( - Jurors::::get(BOB).unwrap(), - JurorInfo { + Participants::::get(BOB).unwrap(), + CourtParticipantInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None, @@ -273,16 +274,16 @@ fn join_court_works_multiple_joins() { assert_eq!(Balances::locks(BOB), vec![the_lock(amount)]); assert_eq!(Balances::locks(ALICE), vec![the_lock(higher_amount)]); assert_eq!( - JurorPool::::get().into_inner(), + CourtPool::::get().into_inner(), vec![ - JurorPoolItem { stake: amount, juror: BOB, consumed_stake: 0 }, - JurorPoolItem { stake: higher_amount, juror: ALICE, consumed_stake: 0 }, + CourtPoolItem { stake: amount, court_participant: BOB, consumed_stake: 0 }, + CourtPoolItem { stake: higher_amount, court_participant: ALICE, consumed_stake: 0 }, ] ); - assert_eq!(Jurors::::iter().count(), 2); + assert_eq!(Participants::::iter().count(), 2); assert_eq!( - Jurors::::get(BOB).unwrap(), - JurorInfo { + Participants::::get(BOB).unwrap(), + CourtParticipantInfo { stake: amount, active_lock: 0u128, prepare_exit_at: None, @@ -290,8 +291,8 @@ fn join_court_works_multiple_joins() { } ); assert_eq!( - Jurors::::get(ALICE).unwrap(), - JurorInfo { + Participants::::get(ALICE).unwrap(), + CourtParticipantInfo { stake: higher_amount, active_lock: 0u128, prepare_exit_at: None, @@ -309,22 +310,23 @@ fn join_court_saves_consumed_stake_and_active_lock_for_double_join() { let consumed_stake = min; let active_lock = min + 1; - Jurors::::insert( + Participants::::insert( ALICE, - JurorInfo { + CourtParticipantInfo { stake: amount, active_lock, prepare_exit_at: None, delegations: Default::default(), }, ); - let juror_pool = vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake }]; - JurorPool::::put::>(juror_pool.try_into().unwrap()); + let juror_pool = + vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake }]; + CourtPool::::put::>(juror_pool.try_into().unwrap()); let higher_amount = amount + 1; assert_ok!(Court::join_court(Origin::signed(ALICE), higher_amount)); - assert_eq!(JurorPool::::get().into_inner()[0].consumed_stake, consumed_stake); - assert_eq!(Jurors::::get(ALICE).unwrap().active_lock, active_lock); + assert_eq!(CourtPool::::get().into_inner()[0].consumed_stake, consumed_stake); + assert_eq!(Participants::::get(ALICE).unwrap().active_lock, active_lock); }); } @@ -377,7 +379,7 @@ fn join_court_fails_juror_needs_to_exit() { assert_noop!( Court::join_court(Origin::signed(ALICE), amount + 1), - Error::::JurorNeedsToExit + Error::::NeedToExit ); }); } @@ -388,7 +390,7 @@ fn join_court_fails_amount_below_lowest_juror() { let min = MinJurorStake::get(); let min_amount = 2 * min; - let max_accounts = JurorPoolOf::::bound(); + let max_accounts = CourtPoolOf::::bound(); let max_amount = min_amount + max_accounts as u128; for i in 1..=max_accounts { let amount = max_amount - i as u128; @@ -396,7 +398,7 @@ fn join_court_fails_amount_below_lowest_juror() { assert_ok!(Court::join_court(Origin::signed(i as u128), amount)); } - assert!(JurorPool::::get().is_full()); + assert!(CourtPool::::get().is_full()); assert_noop!( Court::join_court(Origin::signed(0u128), min_amount - 1), @@ -411,13 +413,13 @@ fn prepare_exit_court_works() { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!( - JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] + CourtPool::::get().into_inner(), + vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] ); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); - System::assert_last_event(Event::JurorPreparedExit { juror: ALICE }.into()); - assert!(JurorPool::::get().into_inner().is_empty()); + System::assert_last_event(Event::ExitPrepared { court_participant: ALICE }.into()); + assert!(CourtPool::::get().into_inner().is_empty()); }); } @@ -427,22 +429,25 @@ fn prepare_exit_court_removes_correct_jurors() { let min = MinJurorStake::get(); let min_amount = 2 * min; - for i in 0..JurorPoolOf::::bound() { + for i in 0..CourtPoolOf::::bound() { let amount = min_amount + i as u128; let juror = i as u128; let _ = Balances::deposit(&juror, amount).unwrap(); assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - for r in 0..JurorPoolOf::::bound() { - let len = JurorPool::::get().into_inner().len(); + for r in 0..CourtPoolOf::::bound() { + let len = CourtPool::::get().into_inner().len(); assert!( - JurorPool::::get().into_inner().iter().any(|item| item.juror == r as u128) + CourtPool::::get() + .into_inner() + .iter() + .any(|item| item.court_participant == r as u128) ); assert_ok!(Court::prepare_exit_court(Origin::signed(r as u128))); - assert_eq!(JurorPool::::get().into_inner().len(), len - 1); - JurorPool::::get().into_inner().iter().for_each(|item| { - assert_ne!(item.juror, r as u128); + assert_eq!(CourtPool::::get().into_inner().len(), len - 1); + CourtPool::::get().into_inner().iter().for_each(|item| { + assert_ne!(item.court_participant, r as u128); }); } }); @@ -454,7 +459,7 @@ fn join_court_binary_search_sorted_insert_works() { let min = MinJurorStake::get(); let min_amount = 2 * min; - let max_accounts = JurorPoolOf::::bound(); + let max_accounts = CourtPoolOf::::bound(); let mut rng = rand::thread_rng(); let mut random_numbers: Vec = (0u32..max_accounts as u32).collect(); random_numbers.shuffle(&mut rng); @@ -467,7 +472,7 @@ fn join_court_binary_search_sorted_insert_works() { } let mut last_stake = 0; - for pool_item in JurorPool::::get().into_inner().iter() { + for pool_item in CourtPool::::get().into_inner().iter() { assert!(pool_item.stake >= last_stake); last_stake = pool_item.stake; } @@ -480,15 +485,15 @@ fn prepare_exit_court_fails_juror_already_prepared_to_exit() { let amount = 2 * BASE; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!( - JurorPool::::get().into_inner(), - vec![JurorPoolItem { stake: amount, juror: ALICE, consumed_stake: 0 }] + CourtPool::::get().into_inner(), + vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] ); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); assert_noop!( Court::prepare_exit_court(Origin::signed(ALICE)), - Error::::JurorAlreadyPreparedExit + Error::::AlreadyPreparedExit ); }); } @@ -496,7 +501,7 @@ fn prepare_exit_court_fails_juror_already_prepared_to_exit() { #[test] fn prepare_exit_court_fails_juror_does_not_exist() { ExtBuilder::default().build().execute_with(|| { - assert!(Jurors::::iter().next().is_none()); + assert!(Participants::::iter().next().is_none()); assert_noop!( Court::prepare_exit_court(Origin::signed(ALICE)), @@ -511,19 +516,24 @@ fn exit_court_works_without_active_lock() { let min = MinJurorStake::get(); let amount = 2 * min; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - assert!(!JurorPool::::get().into_inner().is_empty()); + assert!(!CourtPool::::get().into_inner().is_empty()); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); - assert!(JurorPool::::get().into_inner().is_empty()); - assert!(Jurors::::get(ALICE).is_some()); + assert!(CourtPool::::get().into_inner().is_empty()); + assert!(Participants::::get(ALICE).is_some()); run_blocks(InflationPeriod::get()); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); System::assert_last_event( - Event::JurorExited { juror: ALICE, exit_amount: amount, active_lock: 0u128 }.into(), + Event::ExitedCourt { + court_participant: ALICE, + exit_amount: amount, + active_lock: 0u128, + } + .into(), ); - assert!(Jurors::::iter().next().is_none()); + assert!(Participants::::iter().next().is_none()); assert!(Balances::locks(ALICE).is_empty()); }); } @@ -534,11 +544,11 @@ fn exit_court_works_with_active_lock() { let active_lock = MinJurorStake::get(); let amount = 3 * active_lock; assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); - assert!(!JurorPool::::get().into_inner().is_empty()); + assert!(!CourtPool::::get().into_inner().is_empty()); assert_eq!( - >::get(ALICE).unwrap(), - JurorInfo { + >::get(ALICE).unwrap(), + CourtParticipantInfo { stake: amount, active_lock: 0, prepare_exit_at: None, @@ -546,9 +556,9 @@ fn exit_court_works_with_active_lock() { } ); // assume that `choose_multiple_weighted` has set the active_lock - >::insert( + >::insert( ALICE, - JurorInfo { + CourtParticipantInfo { stake: amount, active_lock, prepare_exit_at: None, @@ -560,18 +570,22 @@ fn exit_court_works_with_active_lock() { let now = >::block_number(); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); - assert!(JurorPool::::get().into_inner().is_empty()); + assert!(CourtPool::::get().into_inner().is_empty()); run_blocks(InflationPeriod::get()); assert_ok!(Court::exit_court(Origin::signed(ALICE), ALICE)); System::assert_last_event( - Event::JurorExited { juror: ALICE, exit_amount: amount - active_lock, active_lock } - .into(), + Event::ExitedCourt { + court_participant: ALICE, + exit_amount: amount - active_lock, + active_lock, + } + .into(), ); assert_eq!( - Jurors::::get(ALICE).unwrap(), - JurorInfo { + Participants::::get(ALICE).unwrap(), + CourtParticipantInfo { stake: active_lock, active_lock, prepare_exit_at: Some(now), @@ -627,7 +641,7 @@ fn exit_court_fails_if_inflation_period_not_over() { #[test] fn vote_works() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -641,12 +655,13 @@ fn vote_works() { ); let slashable = MinJurorStake::get(); let alice_index = - draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); - draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; + draws.binary_search_by_key(&ALICE, |draw| draw.court_participant).unwrap_or_else(|j| j); + draws[alice_index] = + Draw { court_participant: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(court_id, draws); - >::insert( + >::insert( ALICE, - JurorInfo { + CourtParticipantInfo { stake: amount, active_lock: slashable, prepare_exit_at: None, @@ -672,7 +687,10 @@ fn vote_works() { assert_eq!(old_draw, new_draw); } } - assert_eq!(old_draws[alice_index].juror, new_draws[alice_index].juror); + assert_eq!( + old_draws[alice_index].court_participant, + new_draws[alice_index].court_participant + ); assert_eq!(old_draws[alice_index].weight, new_draws[alice_index].weight); assert_eq!(old_draws[alice_index].slashable, new_draws[alice_index].slashable); assert_eq!(old_draws[alice_index].vote, Vote::Drawn); @@ -683,7 +701,7 @@ fn vote_works() { #[test] fn vote_overwrite_works() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -745,7 +763,7 @@ fn vote_fails_if_vote_state_incorrect( vote: crate::Vote<::Hash, crate::DelegatedStakesOf>, ) { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -753,7 +771,7 @@ fn vote_fails_if_vote_state_incorrect( let mut draws = >::get(court_id); assert!(!draws.is_empty()); - draws[0] = Draw { juror: ALICE, weight: 101, vote, slashable: 42u128 }; + draws[0] = Draw { court_participant: ALICE, weight: 101, vote, slashable: 42u128 }; >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -771,11 +789,11 @@ fn vote_fails_if_vote_state_incorrect( #[test] fn vote_fails_if_caller_not_in_draws() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let mut draws = >::get(court_id); - draws.retain(|draw| draw.juror != ALICE); + draws.retain(|draw| draw.court_participant != ALICE); >::insert(court_id, draws); run_to_block(>::get() + 1); @@ -793,7 +811,7 @@ fn vote_fails_if_caller_not_in_draws() { #[test] fn vote_fails_if_not_in_voting_period() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -816,7 +834,7 @@ fn vote_fails_if_not_in_voting_period() { #[test] fn reveal_vote_works() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -830,12 +848,13 @@ fn reveal_vote_works() { ); let slashable = MinJurorStake::get(); let alice_index = - draws.binary_search_by_key(&ALICE, |draw| draw.juror).unwrap_or_else(|j| j); - draws[alice_index] = Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable }; + draws.binary_search_by_key(&ALICE, |draw| draw.court_participant).unwrap_or_else(|j| j); + draws[alice_index] = + Draw { court_participant: ALICE, weight: 1, vote: Vote::Drawn, slashable }; >::insert(court_id, draws); - >::insert( + >::insert( ALICE, - JurorInfo { + CourtParticipantInfo { stake: amount, active_lock: slashable, prepare_exit_at: None, @@ -869,7 +888,10 @@ fn reveal_vote_works() { } assert_eq!(old_draw, new_draw); } - assert_eq!(old_draws[alice_index].juror, new_draws[alice_index].juror); + assert_eq!( + old_draws[alice_index].court_participant, + new_draws[alice_index].court_participant + ); assert_eq!(old_draws[alice_index].weight, new_draws[alice_index].weight); assert_eq!(old_draws[alice_index].slashable, new_draws[alice_index].slashable); assert_eq!(old_draws[alice_index].vote, Vote::Secret { commitment }); @@ -885,7 +907,7 @@ fn reveal_vote_fails_if_caller_not_juror() { run_blocks(CourtVotePeriod::get() + 1); - >::remove(ALICE); + >::remove(ALICE); let vote_item = VoteItem::Outcome(outcome); @@ -940,7 +962,7 @@ fn reveal_vote_fails_if_juror_not_drawn() { run_blocks(CourtVotePeriod::get() + 1); >::mutate(court_id, |draws| { - draws.retain(|draw| draw.juror != ALICE); + draws.retain(|draw| draw.court_participant != ALICE); }); let vote_item = VoteItem::Outcome(outcome); @@ -998,7 +1020,7 @@ fn reveal_vote_fails_if_juror_not_voted() { >::mutate(court_id, |draws| { draws.iter_mut().for_each(|draw| { - if draw.juror == ALICE { + if draw.court_participant == ALICE { draw.vote = Vote::Drawn; } }); @@ -1064,11 +1086,9 @@ fn denounce_vote_works() { let (court_id, commitment, salt) = set_alice_after_vote(outcome.clone()); let old_draws = >::get(court_id); - assert!( - old_draws - .iter() - .any(|draw| { draw.juror == ALICE && matches!(draw.vote, Vote::Secret { .. }) }) - ); + assert!(old_draws.iter().any(|draw| { + draw.court_participant == ALICE && matches!(draw.vote, Vote::Secret { .. }) + })); let free_alice_before = Balances::free_balance(ALICE); let pot_balance_before = Balances::free_balance(&Court::reward_pot(court_id)); @@ -1095,8 +1115,8 @@ fn denounce_vote_works() { let new_draws = >::get(court_id); assert_eq!(old_draws[1..], new_draws[1..]); - assert_eq!(old_draws[0].juror, ALICE); - assert_eq!(old_draws[0].juror, new_draws[0].juror); + assert_eq!(old_draws[0].court_participant, ALICE); + assert_eq!(old_draws[0].court_participant, new_draws[0].court_participant); assert_eq!(old_draws[0].weight, new_draws[0].weight); assert_eq!(old_draws[0].slashable, new_draws[0].slashable); assert_eq!(old_draws[0].vote, Vote::Secret { commitment }); @@ -1105,8 +1125,8 @@ fn denounce_vote_works() { let free_alice_after = Balances::free_balance(ALICE); let slash = old_draws[0].slashable; assert!(!slash.is_zero()); - // slash happens in `reassign_juror_stakes` - // see `reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners` + // slash happens in `reassign_court_stakes` + // see `reassign_court_stakes_slashes_tardy_jurors_and_rewards_winners` assert_eq!(free_alice_after, free_alice_before); let pot_balance_after = Balances::free_balance(&Court::reward_pot(court_id)); @@ -1135,7 +1155,7 @@ fn denounce_vote_fails_if_juror_does_not_exist() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - >::remove(ALICE); + >::remove(ALICE); let vote_item = VoteItem::Outcome(outcome); @@ -1187,7 +1207,7 @@ fn denounce_vote_fails_if_juror_not_drawn() { let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); >::mutate(court_id, |draws| { - draws.retain(|draw| draw.juror != ALICE); + draws.retain(|draw| draw.court_participant != ALICE); }); let vote_item = VoteItem::Outcome(outcome); @@ -1222,7 +1242,7 @@ fn denounce_vote_fails_if_juror_not_voted() { >::mutate(court_id, |draws| { draws.iter_mut().for_each(|draw| { - if draw.juror == ALICE { + if draw.court_participant == ALICE { draw.vote = Vote::Drawn; } }); @@ -1611,9 +1631,9 @@ fn appeal_adds_last_appeal() { } #[test] -fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { +fn reassign_court_stakes_slashes_tardy_jurors_and_rewards_winners() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -1630,22 +1650,32 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { let vote_item = VoteItem::Outcome(outcome); let draws: crate::SelectedDrawsOf = vec![ - Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get() }, Draw { - juror: BOB, + court_participant: ALICE, + weight: 1, + vote: Vote::Drawn, + slashable: MinJurorStake::get(), + }, + Draw { + court_participant: BOB, weight: 1, vote: Vote::Secret { commitment }, slashable: 2 * MinJurorStake::get(), }, Draw { - juror: CHARLIE, + court_participant: CHARLIE, weight: 1, vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: 3 * MinJurorStake::get(), }, - Draw { juror: DAVE, weight: 1, vote: Vote::Drawn, slashable: 4 * MinJurorStake::get() }, Draw { - juror: EVE, + court_participant: DAVE, + weight: 1, + vote: Vote::Drawn, + slashable: 4 * MinJurorStake::get(), + }, + Draw { + court_participant: EVE, weight: 1, vote: Vote::Denounced { commitment, vote_item, salt }, slashable: 5 * MinJurorStake::get(), @@ -1672,7 +1702,7 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { let free_dave_before = Balances::free_balance(&DAVE); let free_eve_before = Balances::free_balance(&EVE); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); let free_alice_after = Balances::free_balance(&ALICE); assert_ne!(free_alice_after, free_alice_before); @@ -1700,17 +1730,17 @@ fn reassign_juror_stakes_slashes_tardy_jurors_and_rewards_winners() { } #[test] -fn reassign_juror_stakes_fails_if_court_not_found() { +fn reassign_court_stakes_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Court::reassign_juror_stakes(Origin::signed(EVE), 0), + Court::reassign_court_stakes(Origin::signed(EVE), 0), Error::::CourtNotFound ); }); } #[test] -fn reassign_juror_stakes_emits_event() { +fn reassign_court_stakes_emits_event() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); @@ -1721,13 +1751,13 @@ fn reassign_juror_stakes_emits_event() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); - System::assert_last_event(Event::JurorStakesReassigned { court_id }.into()); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); + System::assert_last_event(Event::StakesReassigned { court_id }.into()); }); } #[test] -fn reassign_juror_stakes_fails_if_juror_stakes_already_reassigned() { +fn reassign_court_stakes_fails_if_juror_stakes_already_reassigned() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); @@ -1738,17 +1768,17 @@ fn reassign_juror_stakes_fails_if_juror_stakes_already_reassigned() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap().result.unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); assert_noop!( - Court::reassign_juror_stakes(Origin::signed(EVE), court_id), + Court::reassign_court_stakes(Origin::signed(EVE), court_id), Error::::CourtAlreadyReassigned ); }); } #[test] -fn reassign_juror_stakes_updates_court_status() { +fn reassign_court_stakes_updates_court_status() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); @@ -1763,7 +1793,7 @@ fn reassign_juror_stakes_updates_court_status() { let resolution_vote_item = VoteItem::Outcome(resolution_outcome); assert_eq!(court.status, CourtStatus::Closed { winner: resolution_vote_item }); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); let court = >::get(court_id).unwrap(); assert_eq!(court.status, CourtStatus::Reassigned); @@ -1771,7 +1801,7 @@ fn reassign_juror_stakes_updates_court_status() { } #[test] -fn reassign_juror_stakes_removes_draws() { +fn reassign_court_stakes_removes_draws() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); @@ -1785,7 +1815,7 @@ fn reassign_juror_stakes_removes_draws() { let draws = >::get(court_id); assert!(!draws.is_empty()); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); let draws = >::get(court_id); assert!(draws.is_empty()); @@ -1793,20 +1823,20 @@ fn reassign_juror_stakes_removes_draws() { } #[test] -fn reassign_juror_stakes_fails_if_court_not_closed() { +fn reassign_court_stakes_fails_if_court_not_closed() { ExtBuilder::default().build().execute_with(|| { let court_id = initialize_court(); assert_noop!( - Court::reassign_juror_stakes(Origin::signed(EVE), court_id), + Court::reassign_court_stakes(Origin::signed(EVE), court_id), Error::::CourtNotClosed ); }); } #[test] -fn reassign_juror_stakes_decreases_active_lock() { +fn reassign_court_stakes_decreases_active_lock() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -1821,46 +1851,51 @@ fn reassign_juror_stakes_decreases_active_lock() { let commitment = BlakeTwo256::hash_of(&(ALICE, outcome, salt)); let alice_slashable = MinJurorStake::get(); - >::mutate(ALICE, |juror_info| { - if let Some(ref mut info) = juror_info { + >::mutate(ALICE, |p_info| { + if let Some(ref mut info) = p_info { info.active_lock = alice_slashable; } }); let bob_slashable = 2 * MinJurorStake::get(); - >::mutate(BOB, |juror_info| { - if let Some(ref mut juror_info) = juror_info { - juror_info.active_lock = bob_slashable; + >::mutate(BOB, |p_info| { + if let Some(ref mut p_info) = p_info { + p_info.active_lock = bob_slashable; } }); let charlie_slashable = 3 * MinJurorStake::get(); - >::mutate(CHARLIE, |juror_info| { - if let Some(ref mut juror_info) = juror_info { - juror_info.active_lock = charlie_slashable; + >::mutate(CHARLIE, |p_info| { + if let Some(ref mut p_info) = p_info { + p_info.active_lock = charlie_slashable; } }); let dave_slashable = 4 * MinJurorStake::get(); - >::mutate(DAVE, |juror_info| { - if let Some(ref mut juror_info) = juror_info { - juror_info.active_lock = dave_slashable; + >::mutate(DAVE, |p_info| { + if let Some(ref mut p_info) = p_info { + p_info.active_lock = dave_slashable; } }); let draws: crate::SelectedDrawsOf = vec![ - Draw { juror: ALICE, weight: 1, vote: Vote::Drawn, slashable: alice_slashable }, Draw { - juror: BOB, + court_participant: ALICE, + weight: 1, + vote: Vote::Drawn, + slashable: alice_slashable, + }, + Draw { + court_participant: BOB, weight: 1, vote: Vote::Secret { commitment }, slashable: bob_slashable, }, Draw { - juror: CHARLIE, + court_participant: CHARLIE, weight: 1, vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: charlie_slashable, }, Draw { - juror: DAVE, + court_participant: DAVE, weight: 1, vote: Vote::Denounced { commitment, vote_item, salt }, slashable: dave_slashable, @@ -1880,18 +1915,18 @@ fn reassign_juror_stakes_decreases_active_lock() { let market = MarketCommons::market(&market_id).unwrap(); let _ = Court::on_resolution(&market_id, &market).unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); - assert!(>::get(ALICE).unwrap().active_lock.is_zero()); - assert!(>::get(BOB).unwrap().active_lock.is_zero()); - assert!(>::get(CHARLIE).unwrap().active_lock.is_zero()); - assert!(>::get(DAVE).unwrap().active_lock.is_zero()); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); + assert!(>::get(ALICE).unwrap().active_lock.is_zero()); + assert!(>::get(BOB).unwrap().active_lock.is_zero()); + assert!(>::get(CHARLIE).unwrap().active_lock.is_zero()); + assert!(>::get(DAVE).unwrap().active_lock.is_zero()); }); } #[test] -fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { +fn reassign_court_stakes_slashes_loosers_and_awards_winners() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -1917,25 +1952,25 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let draws: crate::SelectedDrawsOf = vec![ Draw { - juror: ALICE, + court_participant: ALICE, weight: 1, vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: alice_slashable, }, Draw { - juror: BOB, + court_participant: BOB, weight: 1, vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_0, salt }, slashable: bob_slashable, }, Draw { - juror: CHARLIE, + court_participant: CHARLIE, weight: 1, vote: Vote::Revealed { commitment, vote_item, salt }, slashable: charlie_slashable, }, Draw { - juror: DAVE, + court_participant: DAVE, weight: 1, vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_1, salt }, slashable: dave_slashable, @@ -1966,7 +2001,7 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { let tardy_or_denounced_value = 5 * MinJurorStake::get(); let _ = Balances::deposit(&reward_pot, tardy_or_denounced_value).unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); let bob_slashed = last_draws[BOB as usize].slashable; let dave_slashed = last_draws[DAVE as usize].slashable; @@ -1993,9 +2028,9 @@ fn reassign_juror_stakes_slashes_loosers_and_awards_winners() { } #[test] -fn reassign_juror_stakes_works_for_delegations() { +fn reassign_court_stakes_works_for_delegations() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -2031,31 +2066,31 @@ fn reassign_juror_stakes_works_for_delegations() { let draws: crate::SelectedDrawsOf = vec![ Draw { - juror: ALICE, + court_participant: ALICE, weight: 1, vote: Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }, slashable: alice_slashable, }, Draw { - juror: EVE, + court_participant: EVE, weight: 1, vote: Vote::Revealed { commitment, vote_item, salt }, slashable: eve_slashable, }, Draw { - juror: BOB, + court_participant: BOB, weight: 1, vote: Vote::Revealed { commitment, vote_item: wrong_vote_item, salt }, slashable: bob_slashable, }, Draw { - juror: CHARLIE, + court_participant: CHARLIE, weight: 1, vote: Vote::Delegated { delegated_stakes: delegated_stakes_charlie.clone() }, slashable: charlie_slashable, }, Draw { - juror: DAVE, + court_participant: DAVE, weight: 1, vote: Vote::Delegated { delegated_stakes: delegated_stakes_dave.clone() }, slashable: dave_slashable, @@ -2087,9 +2122,10 @@ fn reassign_juror_stakes_works_for_delegations() { let tardy_or_denounced_value = 5 * MinJurorStake::get(); let _ = Balances::deposit(&reward_pot, tardy_or_denounced_value).unwrap(); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); - let bob_slashed = last_draws.iter().find(|draw| draw.juror == BOB).unwrap().slashable; + let bob_slashed = + last_draws.iter().find(|draw| draw.court_participant == BOB).unwrap().slashable; let charlie_delegated_bob_slashed = delegated_stakes_charlie.iter().find(|(acc, _)| *acc == BOB).unwrap().1; let dave_delegated_bob_slashed = @@ -2139,9 +2175,9 @@ fn reassign_juror_stakes_works_for_delegations() { } #[test] -fn reassign_juror_stakes_rewards_treasury_if_no_winner() { +fn reassign_court_stakes_rewards_treasury_if_no_winner() { ExtBuilder::default().build().execute_with(|| { - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let court_id = initialize_court(); let amount = MinJurorStake::get() * 100; @@ -2162,25 +2198,25 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { let draws: crate::SelectedDrawsOf = vec![ Draw { - juror: ALICE, + court_participant: ALICE, weight: 1, vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_1.clone(), salt }, slashable: MinJurorStake::get(), }, Draw { - juror: BOB, + court_participant: BOB, weight: 1, vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_0.clone(), salt }, slashable: 2 * MinJurorStake::get(), }, Draw { - juror: CHARLIE, + court_participant: CHARLIE, weight: 1, vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_0, salt }, slashable: 3 * MinJurorStake::get(), }, Draw { - juror: DAVE, + court_participant: DAVE, weight: 1, vote: Vote::Revealed { commitment, vote_item: wrong_vote_item_1, salt }, slashable: 4 * MinJurorStake::get(), @@ -2209,7 +2245,7 @@ fn reassign_juror_stakes_rewards_treasury_if_no_winner() { let treasury_account = Court::treasury_account_id(); let free_treasury_before = Balances::free_balance(&treasury_account); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(EVE), court_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(EVE), court_id)); let alice_slashed = last_draws[ALICE as usize].slashable; let bob_slashed = last_draws[BOB as usize].slashable; @@ -2483,18 +2519,18 @@ fn choose_multiple_weighted_works() { fn select_jurors_updates_juror_consumed_stake() { ExtBuilder::default().build().execute_with(|| { let court_id = initialize_court(); - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); // the last appeal is reserved for global dispute backing let appeal_number = (MaxAppeals::get() - 1) as usize; fill_appeals(court_id, appeal_number); - let jurors = JurorPool::::get(); + let jurors = CourtPool::::get(); let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); let new_draws = Court::select_jurors(appeal_number).unwrap(); let total_draw_slashable = new_draws.iter().map(|draw| draw.slashable).sum::(); - let jurors = JurorPool::::get(); + let jurors = CourtPool::::get(); let consumed_stake_after = jurors.iter().map(|juror| juror.consumed_stake).sum::(); assert_ne!(consumed_stake_before, consumed_stake_after); assert_eq!(consumed_stake_before + total_draw_slashable, consumed_stake_after); @@ -2515,7 +2551,10 @@ fn select_jurors_fails_if_not_enough_jurors(appeal_number: usize) { assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - assert_noop!(Court::select_jurors(appeal_number), Error::::NotEnoughJurorsStake); + assert_noop!( + Court::select_jurors(appeal_number), + Error::::NotEnoughJurorsAndDelegatorsStake + ); }); } @@ -2528,10 +2567,10 @@ fn appeal_reduces_active_lock_from_old_draws() { let old_draws = >::get(court_id); assert!(!old_draws.is_empty()); old_draws.iter().for_each(|draw| { - let juror = draw.juror; - let juror_info = >::get(juror).unwrap(); + let juror = draw.court_participant; + let p_info = >::get(juror).unwrap(); assert_ne!(draw.slashable, 0); - assert_eq!(juror_info.active_lock, draw.slashable); + assert_eq!(p_info.active_lock, draw.slashable); }); run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); @@ -2540,12 +2579,14 @@ fn appeal_reduces_active_lock_from_old_draws() { let new_draws = >::get(court_id); old_draws.iter().for_each(|draw| { - let juror = draw.juror; - let juror_info = >::get(juror).unwrap(); - if let Some(new_draw) = new_draws.iter().find(|new_draw| new_draw.juror == juror) { - assert_eq!(new_draw.slashable, juror_info.active_lock); + let juror = draw.court_participant; + let p_info = >::get(juror).unwrap(); + if let Some(new_draw) = + new_draws.iter().find(|new_draw| new_draw.court_participant == juror) + { + assert_eq!(new_draw.slashable, p_info.active_lock); } else { - assert_eq!(juror_info.active_lock, 0); + assert_eq!(p_info.active_lock, 0); } }); }); @@ -2575,7 +2616,7 @@ fn on_dispute_inserts_draws() { assert_eq!( draws[0], Draw { - juror: ALICE, + court_participant: ALICE, weight: 3, vote: Vote::Drawn, slashable: 3 * MinJurorStake::get() @@ -2583,12 +2624,17 @@ fn on_dispute_inserts_draws() { ); assert_eq!( draws[1], - Draw { juror: BOB, weight: 5, vote: Vote::Drawn, slashable: 5 * MinJurorStake::get() } + Draw { + court_participant: BOB, + weight: 5, + vote: Vote::Drawn, + slashable: 5 * MinJurorStake::get() + } ); assert_eq!( draws[2], Draw { - juror: CHARLIE, + court_participant: CHARLIE, weight: 6, vote: Vote::Drawn, slashable: 6 * MinJurorStake::get() @@ -2596,12 +2642,17 @@ fn on_dispute_inserts_draws() { ); assert_eq!( draws[3], - Draw { juror: DAVE, weight: 7, vote: Vote::Drawn, slashable: 7 * MinJurorStake::get() } + Draw { + court_participant: DAVE, + weight: 7, + vote: Vote::Drawn, + slashable: 7 * MinJurorStake::get() + } ); assert_eq!( draws[4], Draw { - juror: EVE, + court_participant: EVE, weight: 10, vote: Vote::Drawn, slashable: 10 * MinJurorStake::get() @@ -2627,7 +2678,7 @@ fn has_failed_returns_true_for_appealable_court_too_few_jurors() { ExtBuilder::default().build().execute_with(|| { let court_id = initialize_court(); // force empty jurors pool - >::kill(); + >::kill(); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let court = >::get(court_id).unwrap(); @@ -2654,7 +2705,7 @@ fn has_failed_returns_true_for_appealable_court_appeals_full() { fn has_failed_returns_true_for_uninitialized_court() { ExtBuilder::default().build().execute_with(|| { // force empty jurors pool - >::kill(); + >::kill(); let market_id = MarketCommons::push_market(DEFAULT_MARKET).unwrap(); let report_block = 42; MarketCommons::mutate_market(&market_id, |market| { @@ -2702,7 +2753,7 @@ fn prepare_draws(court_id: CourtId, outcomes_with_weights: Vec<(u128, u32)>) { let commitment = BlakeTwo256::hash_of(&(juror, vote_item.clone(), salt)); draws .try_push(Draw { - juror, + court_participant: juror, weight: *weight, vote: Vote::Revealed { commitment, vote_item, salt }, slashable: 0u128, @@ -2793,10 +2844,10 @@ fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { ExtBuilder::default().build().execute_with(|| { run_to_block(123); - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); let nonce_0 = 42u64; - >::put(nonce_0); + >::put(nonce_0); // randomness is mocked and purely based on the nonce // thus a different nonce will result in a different seed (disregarding hash collisions) let first_random_seed = Court::get_random_seed(nonce_0); @@ -2805,7 +2856,7 @@ fn choose_multiple_weighted_returns_different_jurors_with_other_seed() { run_blocks(1); let nonce_1 = 69u64; - >::put(nonce_1); + >::put(nonce_1); let second_random_seed = Court::get_random_seed(nonce_1); assert_ne!(first_random_seed, second_random_seed); @@ -2826,12 +2877,12 @@ fn get_random_seed_returns_equal_seeds_with_equal_nonce() { // this is useful to check that the random seed only depends on the nonce // the same nonce always results in the same seed for testing deterministic let nonce = 42u64; - >::put(nonce); + >::put(nonce); let first_random_seed = Court::get_random_seed(nonce); run_blocks(1); - >::put(nonce); + >::put(nonce); let second_random_seed = Court::get_random_seed(nonce); assert_eq!(first_random_seed, second_random_seed); @@ -2842,13 +2893,13 @@ fn get_random_seed_returns_equal_seeds_with_equal_nonce() { fn random_jurors_returns_a_subset_of_jurors() { ExtBuilder::default().build().execute_with(|| { run_to_block(123); - fill_juror_pool(MaxJurors::get()); + fill_juror_pool(MaxCourtParticipants::get()); - let jurors = >::get(); + let jurors = >::get(); let random_jurors = Court::choose_multiple_weighted(2).unwrap(); for draw in random_jurors { - assert!(jurors.iter().any(|el| el.juror == draw.juror)); + assert!(jurors.iter().any(|el| el.court_participant == draw.court_participant)); } }); } @@ -2856,7 +2907,7 @@ fn random_jurors_returns_a_subset_of_jurors() { #[test] fn handle_inflation_works() { ExtBuilder::default().build().execute_with(|| { - let mut jurors = >::get(); + let mut jurors = >::get(); let mut free_balances_before = BTreeMap::new(); let jurors_list = vec![1000, 10_000, 100_000, 1_000_000, 10_000_000]; for number in jurors_list.iter() { @@ -2864,9 +2915,11 @@ fn handle_inflation_works() { let juror = *number; let _ = Balances::deposit(&juror, stake).unwrap(); free_balances_before.insert(juror, stake); - jurors.try_push(JurorPoolItem { stake, juror, consumed_stake: 0 }).unwrap(); + jurors + .try_push(CourtPoolItem { stake, court_participant: juror, consumed_stake: 0 }) + .unwrap(); } - >::put(jurors.clone()); + >::put(jurors.clone()); let inflation_period = InflationPeriod::get(); run_to_block(inflation_period); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 1ad3a6750..da7240b0b 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -74,7 +74,7 @@ impl VoteItem { } } -/// The general information about a particular juror. +/// The general information about a particular court participant (juror or delegator). #[derive( parity_scale_codec::Decode, parity_scale_codec::Encode, @@ -85,14 +85,15 @@ impl VoteItem { PartialEq, Eq, )] -pub struct JurorInfo { - /// The juror's amount in the stake weighted pool. - /// This amount is used to find a juror with a binary search on the pool. +pub struct CourtParticipantInfo { + /// The court participants amount in the stake weighted pool. + /// This amount is used to find a court participant with a binary search on the pool. pub stake: Balance, /// The current amount of funds which are locked in courts. pub active_lock: Balance, - /// The block number when a juror exit from court was requested. + /// The block number when an exit from court was requested. pub prepare_exit_at: Option, + /// The delegations of the court participant. This determines the account as a delegator. pub delegations: Option, } @@ -126,7 +127,7 @@ pub struct CommitmentMatcher { Eq, )] pub enum Vote { - /// The juror delegated stake to other jurors. + /// The delegator delegated stake to other jurors. Delegated { delegated_stakes: DelegatedStakes }, /// The juror was randomly selected to vote in a specific court case. Drawn, @@ -218,6 +219,7 @@ pub struct CourtInfo { pub appeals: Appeals, /// The information about the lifecycle of this court case. pub cycle_ends: CycleEnds, + /// The type of the vote item. pub vote_item_type: VoteItemType, } @@ -256,7 +258,7 @@ impl } } -/// After a juror was randomly selected to vote in a court case, +/// After a court participant was randomly selected to vote in a court case, /// this information is relevant to handle the post-selection process. #[derive( parity_scale_codec::Decode, @@ -269,8 +271,8 @@ impl Eq, )] pub struct Draw { - /// The juror who was randomly selected. - pub juror: AccountId, + /// The court participant who was randomly selected. + pub court_participant: AccountId, /// The weight of the juror in this court case. /// The higher the weight the more voice the juror has in the final winner decision. pub weight: u32, @@ -292,13 +294,14 @@ pub struct Draw { PartialEq, Eq, )] -pub struct JurorPoolItem { - /// The amount of funds associated to a juror in order to get selected for a court case. +pub struct CourtPoolItem { + /// The amount of funds associated to a court participant + /// in order to get selected for a court case. pub stake: Balance, /// The account which is the juror that might be selected in court cases. - pub juror: AccountId, + pub court_participant: AccountId, /// The consumed amount of the stake for all draws. This is useful to reduce the probability - /// of a juror to be selected again. + /// of a court participant to be selected again. pub consumed_stake: Balance, } diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 1edafbe37..3f7f915c1 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -54,7 +54,7 @@ pub trait WeightInfoZeitgeist { fn denounce_vote(d: u32) -> Weight; fn reveal_vote(d: u32) -> Weight; fn appeal(j: u32, a: u32, r: u32, f: u32) -> Weight; - fn reassign_juror_stakes(d: u32) -> Weight; + fn reassign_court_stakes(d: u32) -> Weight; fn set_inflation() -> Weight; fn handle_inflation(j: u32) -> Weight; fn select_jurors(a: u32) -> Weight; @@ -175,7 +175,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectedDraws (r:1 w:1) // Storage: Court Jurors (r:5 w:5) // Storage: System Account (r:6 w:5) - fn reassign_juror_stakes(d: u32) -> Weight { + fn reassign_court_stakes(d: u32) -> Weight { Weight::from_ref_time(0) // Standard Error: 19_000 .saturating_add(Weight::from_ref_time(44_416_000).saturating_mul(d.into())) diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 5a3a79e1f..84670fa7f 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -44,13 +44,13 @@ use zeitgeist_primitives::{ CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, CourtVotePeriod, ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, InflationPeriod, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, - MaxCategories, MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, - MaxGracePeriod, MaxInRatio, MaxJurors, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, - MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxSubsidyPeriod, MaxSwapFee, - MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, - MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, - OutcomeFactor, OutsiderBond, PmPalletId, RequestInterval, SimpleDisputesPalletId, - SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, + MaxCategories, MaxCourtParticipants, MaxDelegations, MaxDisputeDuration, MaxDisputes, + MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxMarketLifetime, MaxOracleDuration, + MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxSubsidyPeriod, + MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, + MinJurorStake, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, + OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, RequestInterval, + SimpleDisputesPalletId, SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -314,7 +314,7 @@ impl zrml_court::Config for Runtime { type MaxAppeals = MaxAppeals; type MaxDelegations = MaxDelegations; type MaxSelectedDraws = MaxSelectedDraws; - type MaxJurors = MaxJurors; + type MaxCourtParticipants = MaxCourtParticipants; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; type CourtPalletId = CourtPalletId; diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 7f10391fd..7a6f322e8 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3172,12 +3172,14 @@ fn it_resolves_a_disputed_court_market() { let mut draws = zrml_court::SelectedDraws::::get(market_id); for juror in &[juror_0, juror_1, juror_2, juror_3, juror_4, juror_5] { let draw = Draw { - juror: *juror, + court_participant: *juror, weight: 1, vote: Vote::Drawn, slashable: MinJurorStake::get(), }; - let index = draws.binary_search_by_key(juror, |draw| draw.juror).unwrap_or_else(|j| j); + let index = draws + .binary_search_by_key(juror, |draw| draw.court_participant) + .unwrap_or_else(|j| j); draws.try_insert(index, draw).unwrap(); } let old_draws = draws.clone(); @@ -3273,26 +3275,26 @@ fn it_resolves_a_disputed_court_market() { let free_juror_4_before = Balances::free_balance(&juror_4); let free_juror_5_before = Balances::free_balance(&juror_5); - assert_ok!(Court::reassign_juror_stakes(Origin::signed(juror_0), market_id)); + assert_ok!(Court::reassign_court_stakes(Origin::signed(juror_0), market_id)); let free_juror_0_after = Balances::free_balance(&juror_0); let slashable_juror_0 = - old_draws.iter().find(|draw| draw.juror == juror_0).unwrap().slashable; + old_draws.iter().find(|draw| draw.court_participant == juror_0).unwrap().slashable; let free_juror_1_after = Balances::free_balance(&juror_1); let slashable_juror_1 = - old_draws.iter().find(|draw| draw.juror == juror_1).unwrap().slashable; + old_draws.iter().find(|draw| draw.court_participant == juror_1).unwrap().slashable; let free_juror_2_after = Balances::free_balance(&juror_2); let slashable_juror_2 = - old_draws.iter().find(|draw| draw.juror == juror_2).unwrap().slashable; + old_draws.iter().find(|draw| draw.court_participant == juror_2).unwrap().slashable; let free_juror_3_after = Balances::free_balance(&juror_3); let slashable_juror_3 = - old_draws.iter().find(|draw| draw.juror == juror_3).unwrap().slashable; + old_draws.iter().find(|draw| draw.court_participant == juror_3).unwrap().slashable; let free_juror_4_after = Balances::free_balance(&juror_4); let slashable_juror_4 = - old_draws.iter().find(|draw| draw.juror == juror_4).unwrap().slashable; + old_draws.iter().find(|draw| draw.court_participant == juror_4).unwrap().slashable; let free_juror_5_after = Balances::free_balance(&juror_5); let slashable_juror_5 = - old_draws.iter().find(|draw| draw.juror == juror_5).unwrap().slashable; + old_draws.iter().find(|draw| draw.court_participant == juror_5).unwrap().slashable; let mut total_slashed = 0; // juror_1 voted for the wrong outcome => slashed @@ -3336,8 +3338,9 @@ fn simulate_appeal_cycle(market_id: MarketId) { let draws = zrml_court::SelectedDraws::::get(market_id); for draw in &draws { - let commitment = BlakeTwo256::hash_of(&(draw.juror, wrong_vote_item.clone(), salt)); - assert_ok!(Court::vote(Origin::signed(draw.juror), market_id, commitment)); + let commitment = + BlakeTwo256::hash_of(&(draw.court_participant, wrong_vote_item.clone(), salt)); + assert_ok!(Court::vote(Origin::signed(draw.court_participant), market_id, commitment)); } let aggregation_start = court.cycle_ends.vote + 1; @@ -3345,7 +3348,7 @@ fn simulate_appeal_cycle(market_id: MarketId) { for draw in draws { assert_ok!(Court::reveal_vote( - Origin::signed(draw.juror), + Origin::signed(draw.court_participant), market_id, wrong_vote_item.clone(), salt, From b5d51f77c8ad742d5c6317a62024d541feddd0de Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 09:35:13 +0200 Subject: [PATCH 293/368] update changelog for devs --- docs/changelog_for_devs.md | 42 ++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index 28b75a85d..a21f4d9c2 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -8,12 +8,13 @@ and does not represent a complete changelog for the zeitgeistpm/zeitgeist repository. As of 0.3.9, the changelog's format is based on -https://keepachangelog.com/en/1.0.0/ and ⚠️ marks changes that might break + and ⚠️ marks changes that might break components which query the chain's storage, the extrinsics or the runtime APIs/RPC interface. ## v0.3.9 +[#976]: https://github.com/zeitgeistpm/zeitgeist/pull/976 [#937]: https://github.com/zeitgeistpm/zeitgeist/pull/937 [#903]: https://github.com/zeitgeistpm/zeitgeist/pull/903 @@ -21,6 +22,39 @@ APIs/RPC interface. - ⚠️ Add `outsider` field to `MarketBonds` struct. In particular, the `Market` struct's layout has changed ([#903]). +- ⚠️ Add court production implementation ([#976]). + Dispatchable calls are: + - `join_court` - Join the court with a stake to become a juror in order to get the stake-weighted + chance to be selected for decision making. + - `delegate` - Join the court with a stake to become a delegator in order to + delegate the voting power to actively participating jurors. + - `prepare_exit_court` - Prepare as a court participant to leave the court system. + - `exit_court` - Exit the court system in order to get the stake back. + - `vote` - An actively participating juror votes secretely on a specific court case, + in which the juror got selected. + - `denounce_vote` - Denounce a selected and active juror, if the secret and + vote is known before the actual reveal period. + - `reveal_vote` - An actively participating juror reveals the previously casted secret vote. + - `appeal` - After the reveal phase (aggregation period), the jurors decision can be appealed. + - `reassign_juror_stakes` - After the appeal period is over, + losers pay the winners for the jurors and delegators. + - `set_inflation` - Set the yearly inflation rate of the court system. + Events are: + - `JurorJoined` - A juror joined the court system. + - `ExitPrepared` - A court participant prepared to exit the court system. + - `ExitedCourt` - A court participant exited the court system. + - `JurorVoted` - A juror voted on a specific court case. + - `JurorRevealedVote` - A juror revealed the previously casted secret vote. + - `DenouncedJurorVote` - A juror was denounced. + - `DelegatorJoined` - A delegator joined the court system. + - `CourtAppealed` - A court case was appealed. + - `MintedInCourt` - A court participant was rewarded with newly minted tokens. + - `StakesReassigned` - The juror and delegator stakes have been reassigned. + The losing jurors have been slashed. + The winning jurors have been rewarded by the losers. + The losing jurors are those, who did not vote, or did not vote with the plurality, + were denounced or did not reveal their vote. + - `InflationSet` - The yearly inflation rate of the court system was set. ### Fixed @@ -88,7 +122,7 @@ APIs/RPC interface. - Added xTokens pallet to transfer tokens accross chains - Added AssetRegistry pallet to register foreign asset - Added UnknownTokens pallet to handle unknown foreign assets - - More information at https://github.com/zeitgeistpm/zeitgeist/pull/661#top + - More information at - Transformed integer scalar markets to fixed point with ten digits after the decimal point. As soon as this update is deployed, the interpretation of the @@ -187,8 +221,8 @@ APIs/RPC interface. - The `MarketCounter` of the `market-commons` pallet is incremented by one. This means that `MarketCounter` is now equal to the total number of markets ever created, instead of equal to the id of the last market created. For details - regarding this fix, see https://github.com/zeitgeistpm/zeitgeist/pull/636 and - https://github.com/zeitgeistpm/zeitgeist/issues/365. + regarding this fix, see and + . - Made the `min_asset_amount_out` and `max_price` parameters of `swap_exact_amount_in` and the `max_asset_amount_in` and `max_price` From e27c384effe25b4e478728e279964a24fbefd401 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 09:47:36 +0200 Subject: [PATCH 294/368] improve dispute api documentation --- primitives/src/traits/dispute_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/src/traits/dispute_api.rs b/primitives/src/traits/dispute_api.rs index b4ca908b8..7c7accf1c 100644 --- a/primitives/src/traits/dispute_api.rs +++ b/primitives/src/traits/dispute_api.rs @@ -108,7 +108,7 @@ pub trait DisputeApi { market: &MarketOfDisputeApi, ) -> Result, DispatchError>; - /// Called, when a global dispute is started. + /// Initialise a global dispute for the failed market dispute mechanism. /// **May** assume that `market.dispute_mechanism` refers to the calling dispute API. /// /// # Returns From 3996a190a624e24e20afd5d63da7cb81f3973bce Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 10:12:54 +0200 Subject: [PATCH 295/368] correct call filters --- runtime/battery-station/src/lib.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/runtime/battery-station/src/lib.rs b/runtime/battery-station/src/lib.rs index 142afdf62..63681156c 100644 --- a/runtime/battery-station/src/lib.rs +++ b/runtime/battery-station/src/lib.rs @@ -36,6 +36,7 @@ pub use frame_system::{ pub use pallet_author_slot_filter::EligibilityValue; pub use pallet_balances::Call as BalancesCall; use pallet_collective::EnsureProportionMoreThan; +use zrml_prediction_markets::mock::SimpleDisputes; #[cfg(feature = "parachain")] pub use crate::parachain_params::*; @@ -118,20 +119,11 @@ impl Contains for IsCallable { Call::PredictionMarkets(inner_call) => { match inner_call { // Disable Rikiddo and SimpleDisputes markets - create_market { - scoring_rule: RikiddoSigmoidFeeMarketEma, - dispute_mechanism: SimpleDisputes, - .. - } => false, - edit_market { - scoring_rule: RikiddoSigmoidFeeMarketEma, - dispute_mechanism: SimpleDisputes, - .. - } => false, - create_cpmm_market_and_deploy_assets { - dispute_mechanism: SimpleDisputes, - .. - } => false, + create_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, + create_market { dispute_mechanism: SimpleDisputes, .. } => false, + create_cpmm_market_and_deploy_assets { dispute_mechanism: SimpleDisputes, .. } => false, + edit_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, + edit_market { dispute_mechanism: SimpleDisputes, .. } => false, _ => true, } } From ed7b3143779a264d9ee8b8c3295d0bacd6354e85 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 10:42:52 +0200 Subject: [PATCH 296/368] add copyright notice --- zrml/court/src/traits.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/zrml/court/src/traits.rs b/zrml/court/src/traits.rs index 65cfaaa67..cd5f76e7c 100644 --- a/zrml/court/src/traits.rs +++ b/zrml/court/src/traits.rs @@ -1,3 +1,21 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. +// Copyright 2021-2022 Zeitgeist PM LLC. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + use frame_support::dispatch::DispatchError; use crate::types::VoteItem; From 45dcafccd4c60d722a27cc6b15724094dec3c863 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 11:30:04 +0200 Subject: [PATCH 297/368] remove is_outcome and is_binary --- runtime/battery-station/src/lib.rs | 6 ++++-- zrml/court/src/lib.rs | 10 ++++++++-- zrml/court/src/types.rs | 8 -------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/runtime/battery-station/src/lib.rs b/runtime/battery-station/src/lib.rs index 63681156c..91efe1298 100644 --- a/runtime/battery-station/src/lib.rs +++ b/runtime/battery-station/src/lib.rs @@ -36,7 +36,6 @@ pub use frame_system::{ pub use pallet_author_slot_filter::EligibilityValue; pub use pallet_balances::Call as BalancesCall; use pallet_collective::EnsureProportionMoreThan; -use zrml_prediction_markets::mock::SimpleDisputes; #[cfg(feature = "parachain")] pub use crate::parachain_params::*; @@ -121,7 +120,10 @@ impl Contains for IsCallable { // Disable Rikiddo and SimpleDisputes markets create_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, create_market { dispute_mechanism: SimpleDisputes, .. } => false, - create_cpmm_market_and_deploy_assets { dispute_mechanism: SimpleDisputes, .. } => false, + create_cpmm_market_and_deploy_assets { + dispute_mechanism: SimpleDisputes, + .. + } => false, edit_market { scoring_rule: RikiddoSigmoidFeeMarketEma, .. } => false, edit_market { dispute_mechanism: SimpleDisputes, .. } => false, _ => true, diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 86f11fc78..a1cda8f9d 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1647,10 +1647,16 @@ mod pallet { ) -> Result<(), DispatchError> { match court.vote_item_type { VoteItemType::Outcome => { - ensure!(vote_item.is_outcome(), Error::::InvalidVoteItemForOutcomeCourt); + ensure!( + matches!(vote_item, VoteItem::Outcome(_)), + Error::::InvalidVoteItemForOutcomeCourt + ); } VoteItemType::Binary => { - ensure!(vote_item.is_binary(), Error::::InvalidVoteItemForBinaryCourt); + ensure!( + matches!(vote_item, VoteItem::Binary(_)), + Error::::InvalidVoteItemForBinaryCourt + ); } }; diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index da7240b0b..964a305b9 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -64,14 +64,6 @@ impl VoteItem { _ => None, } } - - pub fn is_outcome(&self) -> bool { - matches!(self, Self::Outcome(_)) - } - - pub fn is_binary(&self) -> bool { - matches!(self, Self::Binary(_)) - } } /// The general information about a particular court participant (juror or delegator). From bb25f4ae07195367aaf84fa3c9aa1c974832f4ab Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 13:18:57 +0200 Subject: [PATCH 298/368] remove unnecessary "Court" prefix --- primitives/src/constants/mock.rs | 8 +- runtime/common/src/lib.rs | 10 +-- zrml/court/src/benchmarks.rs | 12 +-- zrml/court/src/lib.rs | 37 ++++------ zrml/court/src/mock.rs | 18 ++--- zrml/court/src/tests.rs | 109 ++++++++++++---------------- zrml/prediction-markets/src/mock.rs | 32 ++++---- 7 files changed, 104 insertions(+), 122 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 409cd830c..4c92e4fc3 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -39,10 +39,10 @@ parameter_types! { pub const BlocksPerYear: BlockNumber = 100; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); pub const RequestInterval: BlockNumber = 15; - pub const CourtVotePeriod: BlockNumber = 3; - pub const CourtAggregationPeriod: BlockNumber = 4; - pub const CourtAppealPeriod: BlockNumber = 5; - pub const CourtLockId: LockIdentifier = *b"zge/cloc"; + pub const VotePeriod: BlockNumber = 3; + pub const AggregationPeriod: BlockNumber = 4; + pub const AppealPeriod: BlockNumber = 5; + pub const LockId: LockIdentifier = *b"zge/cloc"; pub const MaxAppeals: u32 = 4; pub const MaxDelegations: u32 = 5; pub const MaxSelectedDraws: u32 = 510; diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 302101878..031debb13 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -982,11 +982,11 @@ macro_rules! impl_config_traits { type AppealBond = AppealBond; type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; - type CourtVotePeriod = CourtVotePeriod; - type CourtAggregationPeriod = CourtAggregationPeriod; - type CourtAppealPeriod = CourtAppealPeriod; - type CourtLockId = CourtLockId; - type CourtPalletId = CourtPalletId; + type VotePeriod = CourtVotePeriod; + type AggregationPeriod = CourtAggregationPeriod; + type AppealPeriod = CourtAppealPeriod; + type LockId = CourtLockId; + type PalletId = CourtPalletId; type Currency = Balances; type DefaultWinner = DefaultWinner; type DisputeResolution = zrml_prediction_markets::Pallet; diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index d42853c7a..082a931a1 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -479,9 +479,9 @@ benchmarks! { >::put(now + 1u64.saturated_into::()); let new_resolve_at = >::get() - + T::CourtVotePeriod::get() - + T::CourtAggregationPeriod::get() - + T::CourtAppealPeriod::get(); + + T::VotePeriod::get() + + T::AggregationPeriod::get() + + T::AppealPeriod::get(); for i in 0..f { let market_id_i = (i + 100).saturated_into::>(); T::DisputeResolution::add_auto_resolve(&market_id_i, new_resolve_at).unwrap(); @@ -586,9 +586,9 @@ benchmarks! { >::put(pre_vote_end); let appeal_end = pre_vote_end - + T::CourtVotePeriod::get() - + T::CourtAggregationPeriod::get() - + T::CourtAppealPeriod::get(); + + T::VotePeriod::get() + + T::AggregationPeriod::get() + + T::AppealPeriod::get(); for i in 0..r { let market_id_i = (i + 100).saturated_into::>(); diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index a1cda8f9d..7d8a3bfa9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -102,23 +102,23 @@ mod pallet { /// The time in which the jurors can cast their commitment vote. #[pallet::constant] - type CourtVotePeriod: Get; + type VotePeriod: Get; /// The time in which the jurors should reveal their commitment vote. #[pallet::constant] - type CourtAggregationPeriod: Get; + type AggregationPeriod: Get; /// The time in which a court case can get appealed. #[pallet::constant] - type CourtAppealPeriod: Get; + type AppealPeriod: Get; /// The court lock identifier. #[pallet::constant] - type CourtLockId: Get; + type LockId: Get; /// Identifier of this pallet #[pallet::constant] - type CourtPalletId: Get; + type PalletId: Get; /// The currency implementation used to transfer, lock and reserve tokens. type Currency: Currency @@ -642,18 +642,13 @@ mod pallet { ); let (exit_amount, active_lock, weight) = if prev_p_info.active_lock.is_zero() { - T::Currency::remove_lock(T::CourtLockId::get(), &who); + T::Currency::remove_lock(T::LockId::get(), &who); Participants::::remove(&who); (prev_p_info.stake, >::zero(), T::WeightInfo::exit_court_remove()) } else { let active_lock = prev_p_info.active_lock; let exit_amount = prev_p_info.stake.saturating_sub(active_lock); - T::Currency::set_lock( - T::CourtLockId::get(), - &who, - active_lock, - WithdrawReasons::all(), - ); + T::Currency::set_lock(T::LockId::get(), &who, active_lock, WithdrawReasons::all()); prev_p_info.stake = active_lock; Participants::::insert(&who, prev_p_info); @@ -929,9 +924,9 @@ mod pallet { debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { pre_vote_end: request_block, - vote_period: T::CourtVotePeriod::get(), - aggregation_period: T::CourtAggregationPeriod::get(), - appeal_period: T::CourtAppealPeriod::get(), + vote_period: T::VotePeriod::get(), + aggregation_period: T::AggregationPeriod::get(), + appeal_period: T::AppealPeriod::get(), }; // sets cycle_ends one after the other from now court.update_lifecycle(round_timing); @@ -1624,13 +1619,13 @@ mod pallet { /// The reserve ID of the court pallet. #[inline] pub fn reserve_id() -> [u8; 8] { - T::CourtPalletId::get().0 + T::PalletId::get().0 } /// The account ID which is used to reward the correct jurors. #[inline] pub fn reward_pot(court_id: CourtId) -> T::AccountId { - T::CourtPalletId::get().into_sub_account_truncating(court_id) + T::PalletId::get().into_sub_account_truncating(court_id) } /// The account ID of the treasury. @@ -1967,9 +1962,9 @@ mod pallet { debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { pre_vote_end: request_block, - vote_period: T::CourtVotePeriod::get(), - aggregation_period: T::CourtAggregationPeriod::get(), - appeal_period: T::CourtAppealPeriod::get(), + vote_period: T::VotePeriod::get(), + aggregation_period: T::AggregationPeriod::get(), + appeal_period: T::AppealPeriod::get(), }; let vote_item_type = VoteItemType::Outcome; @@ -2309,7 +2304,7 @@ impl Pallet { })?, }; - T::Currency::set_lock(T::CourtLockId::get(), who, amount, WithdrawReasons::all()); + T::Currency::set_lock(T::LockId::get(), who, amount, WithdrawReasons::all()); let pool_len = pool.len() as u32; CourtPool::::put(pool); diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 472247718..27de62a12 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -38,10 +38,10 @@ use sp_runtime::{ }; use zeitgeist_primitives::{ constants::mock::{ - AppealBond, BlockHashCount, BlocksPerYear, CourtAggregationPeriod, CourtAppealPeriod, - CourtLockId, CourtPalletId, CourtVotePeriod, InflationPeriod, MaxAppeals, MaxApprovals, - MaxCourtParticipants, MaxDelegations, MaxReserves, MaxSelectedDraws, MinJurorStake, - MinimumPeriod, PmPalletId, RequestInterval, BASE, + AggregationPeriod, AppealBond, AppealPeriod, BlockHashCount, BlocksPerYear, CourtPalletId, + InflationPeriod, LockId, MaxAppeals, MaxApprovals, MaxCourtParticipants, MaxDelegations, + MaxReserves, MaxSelectedDraws, MinJurorStake, MinimumPeriod, PmPalletId, RequestInterval, + VotePeriod, BASE, }, traits::DisputeResolutionApi, types::{ @@ -184,11 +184,11 @@ impl crate::Config for Runtime { type AppealBond = AppealBond; type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; - type CourtLockId = CourtLockId; + type LockId = LockId; type Currency = Balances; - type CourtVotePeriod = CourtVotePeriod; - type CourtAggregationPeriod = CourtAggregationPeriod; - type CourtAppealPeriod = CourtAppealPeriod; + type VotePeriod = VotePeriod; + type AggregationPeriod = AggregationPeriod; + type AppealPeriod = AppealPeriod; type DefaultWinner = DefaultWinner; type DisputeResolution = MockResolution; type Event = Event; @@ -200,7 +200,7 @@ impl crate::Config for Runtime { type MaxCourtParticipants = MaxCourtParticipants; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; - type CourtPalletId = CourtPalletId; + type PalletId = CourtPalletId; type Random = MockStorage; type RequestInterval = RequestInterval; type Slash = Treasury; diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 8c608c1ac..ee6921ace 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -45,8 +45,8 @@ use test_case::test_case; use zeitgeist_primitives::{ constants::{ mock::{ - AppealBond, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtVotePeriod, - InflationPeriod, MaxAppeals, MaxCourtParticipants, MinJurorStake, RequestInterval, + AggregationPeriod, AppealBond, AppealPeriod, InflationPeriod, LockId, MaxAppeals, + MaxCourtParticipants, MinJurorStake, RequestInterval, VotePeriod, }, BASE, }, @@ -170,7 +170,7 @@ fn set_alice_after_vote( } fn the_lock(amount: u128) -> BalanceLock { - BalanceLock { id: CourtLockId::get(), amount, reasons: pallet_balances::Reasons::All } + BalanceLock { id: LockId::get(), amount, reasons: pallet_balances::Reasons::All } } #[test] @@ -819,7 +819,7 @@ fn vote_fails_if_not_in_voting_period() { put_alice_in_draw(court_id, amount); - run_to_block(>::get() + CourtVotePeriod::get() + 1); + run_to_block(>::get() + VotePeriod::get() + 1); let outcome = OutcomeReport::Scalar(42u128); let salt = ::Hash::default(); @@ -873,7 +873,7 @@ fn reveal_vote_works() { let old_draws = >::get(court_id); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); assert_ok!(Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item.clone(), salt,)); System::assert_last_event( @@ -905,7 +905,7 @@ fn reveal_vote_fails_if_caller_not_juror() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); >::remove(ALICE); @@ -923,7 +923,7 @@ fn reveal_vote_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); >::remove(court_id); @@ -942,7 +942,7 @@ fn reveal_vote_fails_if_not_in_aggregation_period() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let vote_item = VoteItem::Outcome(outcome); @@ -959,7 +959,7 @@ fn reveal_vote_fails_if_juror_not_drawn() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); >::mutate(court_id, |draws| { draws.retain(|draw| draw.court_participant != ALICE); @@ -980,7 +980,7 @@ fn reveal_vote_fails_for_invalid_reveal() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); let invalid_outcome = OutcomeReport::Scalar(43u128); let invalid_vote_item = VoteItem::Outcome(invalid_outcome); @@ -997,7 +997,7 @@ fn reveal_vote_fails_for_invalid_salt() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, correct_salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); let incorrect_salt: ::Hash = [42; 32].into(); assert_ne!(correct_salt, incorrect_salt); @@ -1016,7 +1016,7 @@ fn reveal_vote_fails_if_juror_not_voted() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); >::mutate(court_id, |draws| { draws.iter_mut().for_each(|draw| { @@ -1041,7 +1041,7 @@ fn reveal_vote_fails_if_already_revealed() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); let vote_item = VoteItem::Outcome(outcome); @@ -1070,7 +1070,7 @@ fn reveal_vote_fails_if_already_denounced() { salt )); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); assert_noop!( Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), @@ -1189,7 +1189,7 @@ fn denounce_vote_fails_if_not_in_voting_period() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); let vote_item = VoteItem::Outcome(outcome); @@ -1263,7 +1263,7 @@ fn denounce_vote_fails_if_vote_already_revealed() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, salt) = set_alice_after_vote(outcome.clone()); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); let vote_item = VoteItem::Outcome(outcome); @@ -1307,7 +1307,7 @@ fn appeal_updates_cycle_ends() { let last_court = >::get(court_id).unwrap(); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1317,17 +1317,14 @@ fn appeal_updates_cycle_ends() { let request_block = >::get(); assert!(now < request_block); assert_eq!(court.cycle_ends.pre_vote, request_block); - assert_eq!(court.cycle_ends.vote, request_block + CourtVotePeriod::get()); + assert_eq!(court.cycle_ends.vote, request_block + VotePeriod::get()); assert_eq!( court.cycle_ends.aggregation, - request_block + CourtVotePeriod::get() + CourtAggregationPeriod::get() + request_block + VotePeriod::get() + AggregationPeriod::get() ); assert_eq!( court.cycle_ends.appeal, - request_block - + CourtVotePeriod::get() - + CourtAggregationPeriod::get() - + CourtAppealPeriod::get() + request_block + VotePeriod::get() + AggregationPeriod::get() + AppealPeriod::get() ); assert!(last_court.cycle_ends.pre_vote < court.cycle_ends.pre_vote); @@ -1343,7 +1340,7 @@ fn appeal_reserves_get_appeal_bond() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let free_charlie_before = Balances::free_balance(CHARLIE); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1362,7 +1359,7 @@ fn appeal_emits_event() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1379,7 +1376,7 @@ fn appeal_shifts_auto_resolve() { let resolve_at_0 = >::get(court_id).unwrap().cycle_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![0]); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1399,7 +1396,7 @@ fn appeal_overrides_last_draws() { let last_draws = >::get(court_id); assert!(!last_draws.len().is_zero()); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1418,7 +1415,7 @@ fn appeal_draws_total_weight_is_correct() { let last_draws_total_weight = last_draws.iter().map(|draw| draw.weight).sum::(); assert_eq!(last_draws_total_weight, Court::necessary_draws_weight(0usize) as u32); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1435,7 +1432,7 @@ fn appeal_get_latest_resolved_outcome_changes() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1458,13 +1455,13 @@ fn appeal_get_latest_resolved_outcome_changes() { put_alice_in_draw(court_id, MinJurorStake::get()); assert_ok!(Court::vote(Origin::signed(ALICE), court_id, commitment)); - run_blocks(CourtVotePeriod::get() + 1); + run_blocks(VotePeriod::get() + 1); let vote_item = VoteItem::Outcome(outcome); assert_ok!(Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item.clone(), salt)); - run_blocks(CourtAggregationPeriod::get() + 1); + run_blocks(AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -1496,7 +1493,7 @@ fn appeal_fails_if_appeal_bond_exceeds_balance() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_noop!( Court::appeal(Origin::signed(POOR_PAUL), court_id), @@ -1513,7 +1510,7 @@ fn appeal_fails_if_max_appeals_reached() { fill_appeals(court_id, MaxAppeals::get() as usize); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_noop!( Court::appeal(Origin::signed(CHARLIE), court_id), @@ -1529,7 +1526,7 @@ fn check_appealable_market_fails_if_market_not_found() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let court = >::get(court_id).unwrap(); MarketCommons::remove_market(&court_id).unwrap(); @@ -1548,7 +1545,7 @@ fn check_appealable_market_fails_if_dispute_mechanism_wrong() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let court = >::get(court_id).unwrap(); @@ -1573,7 +1570,7 @@ fn check_appealable_market_fails_if_not_in_appeal_period() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get()); + run_blocks(VotePeriod::get()); let court = >::get(court_id).unwrap(); @@ -1590,7 +1587,7 @@ fn appeal_last_appeal_just_removes_auto_resolve() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); fill_appeals(court_id, (MaxAppeals::get() - 1) as usize); @@ -1612,7 +1609,7 @@ fn appeal_adds_last_appeal() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); fill_appeals(court_id, (MaxAppeals::get() - 1) as usize); @@ -1688,9 +1685,7 @@ fn reassign_court_stakes_slashes_tardy_jurors_and_rewards_winners() { run_to_block(>::get() + 1); - run_blocks( - CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, - ); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + AppealPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -1745,7 +1740,7 @@ fn reassign_court_stakes_emits_event() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -1762,7 +1757,7 @@ fn reassign_court_stakes_fails_if_juror_stakes_already_reassigned() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -1783,7 +1778,7 @@ fn reassign_court_stakes_updates_court_status() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -1806,7 +1801,7 @@ fn reassign_court_stakes_removes_draws() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -1907,9 +1902,7 @@ fn reassign_court_stakes_decreases_active_lock() { run_to_block(>::get() + 1); - run_blocks( - CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, - ); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + AppealPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -1983,9 +1976,7 @@ fn reassign_court_stakes_slashes_loosers_and_awards_winners() { run_to_block(>::get() + 1); - run_blocks( - CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, - ); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + AppealPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -2103,9 +2094,7 @@ fn reassign_court_stakes_works_for_delegations() { run_to_block(>::get() + 1); - run_blocks( - CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, - ); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + AppealPeriod::get() + 1); let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); @@ -2229,9 +2218,7 @@ fn reassign_court_stakes_rewards_treasury_if_no_winner() { run_to_block(>::get() + 1); - run_blocks( - CourtVotePeriod::get() + CourtAggregationPeriod::get() + CourtAppealPeriod::get() + 1, - ); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + AppealPeriod::get() + 1); let mut court = >::get(court_id).unwrap(); court.status = CourtStatus::Closed { winner: vote_item }; @@ -2573,7 +2560,7 @@ fn appeal_reduces_active_lock_from_old_draws() { assert_eq!(p_info.active_lock, draw.slashable); }); - run_blocks(CourtVotePeriod::get() + CourtAggregationPeriod::get() + 1); + run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); @@ -2600,9 +2587,9 @@ fn on_dispute_creates_correct_court_info() { let cycle_ends = court.cycle_ends; let request_block = >::get(); assert_eq!(cycle_ends.pre_vote, request_block); - assert_eq!(cycle_ends.vote, cycle_ends.pre_vote + CourtVotePeriod::get()); - assert_eq!(cycle_ends.aggregation, cycle_ends.vote + CourtAggregationPeriod::get()); - assert_eq!(cycle_ends.appeal, cycle_ends.aggregation + CourtAppealPeriod::get()); + assert_eq!(cycle_ends.vote, cycle_ends.pre_vote + VotePeriod::get()); + assert_eq!(cycle_ends.aggregation, cycle_ends.vote + AggregationPeriod::get()); + assert_eq!(cycle_ends.appeal, cycle_ends.aggregation + AppealPeriod::get()); assert_eq!(court.status, CourtStatus::Open); assert!(court.appeals.is_empty()); }); diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 84670fa7f..4cc608e83 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -40,17 +40,17 @@ use sp_runtime::{ use substrate_fixed::{types::extra::U33, FixedI128, FixedU128}; use zeitgeist_primitives::{ constants::mock::{ - AppealBond, AuthorizedPalletId, BalanceFractionalDecimals, BlockHashCount, BlocksPerYear, - CorrectionPeriod, CourtAggregationPeriod, CourtAppealPeriod, CourtLockId, CourtPalletId, - CourtVotePeriod, ExistentialDeposit, ExistentialDeposits, ExitFee, GetNativeCurrencyId, - InflationPeriod, LiquidityMiningPalletId, MaxAppeals, MaxApprovals, MaxAssets, - MaxCategories, MaxCourtParticipants, MaxDelegations, MaxDisputeDuration, MaxDisputes, - MaxEditReasonLen, MaxGracePeriod, MaxInRatio, MaxMarketLifetime, MaxOracleDuration, - MaxOutRatio, MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxSubsidyPeriod, - MaxSwapFee, MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, - MinJurorStake, MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, - OutcomeBond, OutcomeFactor, OutsiderBond, PmPalletId, RequestInterval, - SimpleDisputesPalletId, SwapsPalletId, TreasuryPalletId, BASE, CENT, MILLISECS_PER_BLOCK, + AggregationPeriod, AppealBond, AppealPeriod, AuthorizedPalletId, BalanceFractionalDecimals, + BlockHashCount, BlocksPerYear, CorrectionPeriod, CourtPalletId, ExistentialDeposit, + ExistentialDeposits, ExitFee, GetNativeCurrencyId, InflationPeriod, + LiquidityMiningPalletId, LockId, MaxAppeals, MaxApprovals, MaxAssets, MaxCategories, + MaxCourtParticipants, MaxDelegations, MaxDisputeDuration, MaxDisputes, MaxEditReasonLen, + MaxGracePeriod, MaxInRatio, MaxMarketLifetime, MaxOracleDuration, MaxOutRatio, + MaxRejectReasonLen, MaxReserves, MaxSelectedDraws, MaxSubsidyPeriod, MaxSwapFee, + MaxTotalWeight, MaxWeight, MinAssets, MinCategories, MinDisputeDuration, MinJurorStake, + MinOracleDuration, MinSubsidy, MinSubsidyPeriod, MinWeight, MinimumPeriod, OutcomeBond, + OutcomeFactor, OutsiderBond, PmPalletId, RequestInterval, SimpleDisputesPalletId, + SwapsPalletId, TreasuryPalletId, VotePeriod, BASE, CENT, MILLISECS_PER_BLOCK, }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, @@ -302,10 +302,10 @@ impl zrml_court::Config for Runtime { type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; type DisputeResolution = prediction_markets::Pallet; - type CourtVotePeriod = CourtVotePeriod; - type CourtAggregationPeriod = CourtAggregationPeriod; - type CourtAppealPeriod = CourtAppealPeriod; - type CourtLockId = CourtLockId; + type VotePeriod = VotePeriod; + type AggregationPeriod = AggregationPeriod; + type AppealPeriod = AppealPeriod; + type LockId = LockId; type Currency = Balances; type DefaultWinner = DefaultWinner; type Event = Event; @@ -317,7 +317,7 @@ impl zrml_court::Config for Runtime { type MaxCourtParticipants = MaxCourtParticipants; type MinJurorStake = MinJurorStake; type MonetaryGovernanceOrigin = EnsureRoot; - type CourtPalletId = CourtPalletId; + type PalletId = CourtPalletId; type Random = RandomnessCollectiveFlip; type RequestInterval = RequestInterval; type Slash = Treasury; From 3c0c75727e61da014a5aa373cee4c25fef877fb4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 13:33:15 +0200 Subject: [PATCH 299/368] update copyright notices --- zrml/court/src/types.rs | 1 + zrml/simple-disputes/src/benchmarks.rs | 1 + zrml/simple-disputes/src/weights.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 964a305b9..df041ebe3 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/simple-disputes/src/benchmarks.rs b/zrml/simple-disputes/src/benchmarks.rs index 3fb5db8f4..158830f43 100644 --- a/zrml/simple-disputes/src/benchmarks.rs +++ b/zrml/simple-disputes/src/benchmarks.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/simple-disputes/src/weights.rs b/zrml/simple-disputes/src/weights.rs index f4233871d..8337d6921 100644 --- a/zrml/simple-disputes/src/weights.rs +++ b/zrml/simple-disputes/src/weights.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. From b122ac1493dd526fc4d689763cd5194260c6cf63 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 13:36:28 +0200 Subject: [PATCH 300/368] remove front end comment --- zrml/court/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7d8a3bfa9..49eb79f8e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1851,11 +1851,6 @@ mod pallet { // Check if the (juror, vote_item, salt) combination matches the secret hash of the vote. pub(crate) fn is_valid(commitment_matcher: CommitmentMatcherOf) -> DispatchResult { - // market id and current appeal number is part of salt generation - // salt should be signed by the juror (court_id ++ appeal number) - // salt can be reproduced only be the juror address - // with knowing court_id and appeal number - // so even if the salt is forgotten it can be reproduced only by the juror let CommitmentMatcher { hashed: commitment, raw: RawCommitment { juror, vote_item, salt }, From 40c79b100bfd5cedb2433c942f33a5118b012590 Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 15 May 2023 14:22:19 +0200 Subject: [PATCH 301/368] Update zrml/court/README.md Co-authored-by: Malte Kliemann --- zrml/court/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index 07d2e6fc8..8be9091ac 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -1,6 +1,6 @@ # Court -A pallet for stake-weighted, plurality decision making. +A pallet for stake-weighted plurality decision making. ## Overview From beb7caf57b83760862444b08b701b088d5b6649d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 14:46:48 +0200 Subject: [PATCH 302/368] npx prettier court readme --- zrml/court/README.md | 60 ++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index 07d2e6fc8..8eafce47c 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -4,16 +4,16 @@ A pallet for stake-weighted, plurality decision making. ## Overview -The court system is responsible for ensuring that the truth is added to the blockchain. -Prediction markets, which depend on accurate information, -reward traders who base their decisions on truthful data. -If someone provides false information, they will be punished, -while those who share accurate information will be rewarded. +The court system is responsible for ensuring that the truth is added to the +blockchain. Prediction markets, which depend on accurate information, reward +traders who base their decisions on truthful data. If someone provides false +information, they will be punished, while those who share accurate information +will be rewarded. ## Terminology -- `aggregation period` - The period in which the actively participating jurors - need to reveal their vote secrets. +- `aggregation period` - The period in which the actively participating jurors + need to reveal their vote secrets. ## Interface @@ -21,20 +21,23 @@ while those who share accurate information will be rewarded. #### Public Dispatches -- `join_court` - Join the court with a stake to become a juror in order to get the stake-weighted - chance to be selected for decision making. -- `delegate` - Join the court with a stake to become a delegator in order to - delegate the voting power to actively participating jurors. -- `prepare_exit_court` - Prepare as a court participant to leave the court system. +- `join_court` - Join the court with a stake to become a juror in order to get + the stake-weighted chance to be selected for decision making. +- `delegate` - Join the court with a stake to become a delegator in order to + delegate the voting power to actively participating jurors. +- `prepare_exit_court` - Prepare as a court participant to leave the court + system. - `exit_court` - Exit the court system in order to get the stake back. -- `vote` - An actively participating juror votes secretely on a specific court case, - in which the juror got selected. -- `denounce_vote` - Denounce a selected and active juror, if the secret and - vote is known before the actual reveal period. -- `reveal_vote` - An actively participating juror reveals the previously casted secret vote. -- `appeal` - After the reveal phase (aggregation period), the jurors decision can be appealed. -- `reassign_juror_stakes` - After the appeal period is over, - losers pay the winners for the jurors and delegators. +- `vote` - An actively participating juror votes secretely on a specific court + case, in which the juror got selected. +- `denounce_vote` - Denounce a selected and active juror, if the secret and vote + is known before the actual reveal period. +- `reveal_vote` - An actively participating juror reveals the previously casted + secret vote. +- `appeal` - After the reveal phase (aggregation period), the jurors decision + can be appealed. +- `reassign_juror_stakes` - After the appeal period is over, losers pay the + winners for the jurors and delegators. #### `MonetaryGovernanceOrigin` Dispatches @@ -43,11 +46,14 @@ while those who share accurate information will be rewarded. #### Private Pallet API - `on_dispute` - Initialise a new court case. -- `on_resolution` - Resolve an existing court case and return the decision winner. +- `on_resolution` - Resolve an existing court case and return the decision + winner. - `exchange` - Slash the unjustified appealers, put those funds to the treasury - and unreserve the justified appeal bonds. -- `get_auto_resolve` - Get the block number when `on_resolution` is evoked for a court case. -- `has_failed` - Determine whether the court mechanism failed - to meet the preconditions to start a court case. -- `on_global_dispute` - Prepare the global dispute if the court mechanism has failed. -- `clear` - Clean up the storage items of a specific court case. \ No newline at end of file + and unreserve the justified appeal bonds. +- `get_auto_resolve` - Get the block number when `on_resolution` is evoked for a + court case. +- `has_failed` - Determine whether the court mechanism failed to meet the + preconditions to start a court case. +- `on_global_dispute` - Prepare the global dispute if the court mechanism has + failed. +- `clear` - Clean up the storage items of a specific court case. From d623ec7a2364b55856511e6243aef5923b6ea66e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 15:08:08 +0200 Subject: [PATCH 303/368] improve style of terminology --- zrml/court/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index 0364ad94c..b998f0d13 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -2,6 +2,11 @@ A pallet for stake-weighted plurality decision making. +- [`Call`]() +- [`Config`]() +- [`Error`]() +- [`Event`]() + ## Overview The court system is responsible for ensuring that the truth is added to the @@ -12,7 +17,7 @@ will be rewarded. ## Terminology -- `aggregation period` - The period in which the actively participating jurors +- **Aggregation Period:** The period in which the actively participating jurors need to reveal their vote secrets. ## Interface From 77e4f53f5179b0424330197dc271ac21189321f4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 15:28:41 +0200 Subject: [PATCH 304/368] update readme --- zrml/court/README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index b998f0d13..fde326e9f 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -47,18 +47,3 @@ will be rewarded. #### `MonetaryGovernanceOrigin` Dispatches - `set_inflation` - Set the yearly inflation rate of the court system. - -#### Private Pallet API - -- `on_dispute` - Initialise a new court case. -- `on_resolution` - Resolve an existing court case and return the decision - winner. -- `exchange` - Slash the unjustified appealers, put those funds to the treasury - and unreserve the justified appeal bonds. -- `get_auto_resolve` - Get the block number when `on_resolution` is evoked for a - court case. -- `has_failed` - Determine whether the court mechanism failed to meet the - preconditions to start a court case. -- `on_global_dispute` - Prepare the global dispute if the court mechanism has - failed. -- `clear` - Clean up the storage items of a specific court case. From c74938be1a9c70ba1d1aa730c465dcf3adbe020f Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 15:31:24 +0200 Subject: [PATCH 305/368] remove unnecessary impl block --- zrml/court/src/lib.rs | 184 +++++++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 90 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 49eb79f8e..68bc84e01 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1101,6 +1101,100 @@ mod pallet { where T: Config, { + fn do_join_court( + who: &T::AccountId, + amount: BalanceOf, + delegations: Option>, + ) -> Result { + ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); + let free_balance = T::Currency::free_balance(who); + ensure!(amount <= free_balance, Error::::AmountExceedsBalance); + + let mut pool = CourtPool::::get(); + + let (active_lock, consumed_stake) = if let Some(prev_p_info) = + >::get(who) + { + ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); + let (index, pool_item) = Self::get_pool_item(&pool, prev_p_info.stake, who) + .ok_or(Error::::NeedToExit)?; + debug_assert!( + prev_p_info.prepare_exit_at.is_none(), + "If the pool item is found, the prepare_exit_at could have never been written." + ); + let consumed_stake = pool_item.consumed_stake; + pool.remove(index); + (prev_p_info.active_lock, consumed_stake) + } else { + if pool.is_full() { + let lowest_item = pool.first(); + let lowest_stake = lowest_item + .map(|pool_item| pool_item.stake) + .unwrap_or_else(>::zero); + debug_assert!({ + let mut sorted = pool.clone(); + sorted.sort_by_key(|pool_item| { + (pool_item.stake, pool_item.court_participant.clone()) + }); + pool.len() == sorted.len() + && pool + .iter() + .zip(sorted.iter()) + .all(|(a, b)| lowest_stake <= a.stake && a == b) + }); + ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); + // remove the lowest staked court participant + pool.remove(0); + } + (>::zero(), >::zero()) + }; + + match pool.binary_search_by_key(&(amount, who), |pool_item| { + (pool_item.stake, &pool_item.court_participant) + }) { + Ok(_) => { + debug_assert!( + false, + "This should never happen, because we are removing the court participant \ + above." + ); + return Err(Error::::CourtParticipantTwiceInPool.into()); + } + Err(i) => pool + .try_insert( + i, + CourtPoolItem { + stake: amount, + court_participant: who.clone(), + consumed_stake, + }, + ) + .map_err(|_| { + debug_assert!( + false, + "This should never happen, because we are removing the lowest staked \ + court participant above." + ); + Error::::MaxCourtParticipantsReached + })?, + }; + + T::Currency::set_lock(T::LockId::get(), who, amount, WithdrawReasons::all()); + + let pool_len = pool.len() as u32; + CourtPool::::put(pool); + + let p_info = CourtParticipantInfoOf:: { + stake: amount, + active_lock, + prepare_exit_at: None, + delegations, + }; + >::insert(who, p_info); + + Ok(pool_len) + } + // Handle the external incentivisation of the court system. pub(crate) fn handle_inflation(now: T::BlockNumber) -> Weight { let inflation_period = T::InflationPeriod::get(); @@ -2225,93 +2319,3 @@ mod pallet { ) } } - -impl Pallet { - fn do_join_court( - who: &T::AccountId, - amount: BalanceOf, - delegations: Option>, - ) -> Result { - ensure!(amount >= T::MinJurorStake::get(), Error::::BelowMinJurorStake); - let free_balance = T::Currency::free_balance(who); - ensure!(amount <= free_balance, Error::::AmountExceedsBalance); - - let mut pool = CourtPool::::get(); - - let (active_lock, consumed_stake) = if let Some(prev_p_info) = >::get(who) { - ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); - let (index, pool_item) = - Self::get_pool_item(&pool, prev_p_info.stake, who).ok_or(Error::::NeedToExit)?; - debug_assert!( - prev_p_info.prepare_exit_at.is_none(), - "If the pool item is found, the prepare_exit_at could have never been written." - ); - let consumed_stake = pool_item.consumed_stake; - pool.remove(index); - (prev_p_info.active_lock, consumed_stake) - } else { - if pool.is_full() { - let lowest_item = pool.first(); - let lowest_stake = lowest_item - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero); - debug_assert!({ - let mut sorted = pool.clone(); - sorted.sort_by_key(|pool_item| { - (pool_item.stake, pool_item.court_participant.clone()) - }); - pool.len() == sorted.len() - && pool - .iter() - .zip(sorted.iter()) - .all(|(a, b)| lowest_stake <= a.stake && a == b) - }); - ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); - // remove the lowest staked court participant - pool.remove(0); - } - (>::zero(), >::zero()) - }; - - match pool.binary_search_by_key(&(amount, who), |pool_item| { - (pool_item.stake, &pool_item.court_participant) - }) { - Ok(_) => { - debug_assert!( - false, - "This should never happen, because we are removing the court participant \ - above." - ); - return Err(Error::::CourtParticipantTwiceInPool.into()); - } - Err(i) => pool - .try_insert( - i, - CourtPoolItem { stake: amount, court_participant: who.clone(), consumed_stake }, - ) - .map_err(|_| { - debug_assert!( - false, - "This should never happen, because we are removing the lowest staked \ - court participant above." - ); - Error::::MaxCourtParticipantsReached - })?, - }; - - T::Currency::set_lock(T::LockId::get(), who, amount, WithdrawReasons::all()); - - let pool_len = pool.len() as u32; - CourtPool::::put(pool); - - let p_info = CourtParticipantInfoOf:: { - stake: amount, - active_lock, - prepare_exit_at: None, - delegations, - }; - >::insert(who, p_info); - - Ok(pool_len) - } -} From 291eb96f82a015ae3efdbce7b82b7756d48eefa5 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 15:33:34 +0200 Subject: [PATCH 306/368] update comment --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 68bc84e01..fa989c0d4 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -190,7 +190,7 @@ mod pallet { /// Handler for slashed funds. type Slash: OnUnbalanced>; - /// Slashed funds are send to the treasury + /// The treasury pallet identifier. #[pallet::constant] type TreasuryPalletId: Get; From 6486cf97c7463275e34b326ae60b36f6d6f8ce01 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 15 May 2023 15:34:18 +0200 Subject: [PATCH 307/368] remove todo --- zrml/court/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index fa989c0d4..9e4911afa 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -2137,7 +2137,6 @@ mod pallet { } } - // TODO all funds to treasury? let res = ResultWithWeightInfo { result: overall_imbalance, weight: T::WeightInfo::exchange(appeals_len), From 43662851971b4a975600c1e57bb665f33120b01c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 11:25:48 +0200 Subject: [PATCH 308/368] make reward_pot package private --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9e4911afa..78f522167 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1718,7 +1718,7 @@ mod pallet { /// The account ID which is used to reward the correct jurors. #[inline] - pub fn reward_pot(court_id: CourtId) -> T::AccountId { + pub(crate) fn reward_pot(court_id: CourtId) -> T::AccountId { T::PalletId::get().into_sub_account_truncating(court_id) } From 99fc4cf5992e3d3fcdfbd77b6d6def950cbe291f Mon Sep 17 00:00:00 2001 From: Chralt Date: Tue, 16 May 2023 12:30:28 +0200 Subject: [PATCH 309/368] Update zrml/court/src/lib.rs Co-authored-by: Malte Kliemann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 78f522167..bec1321da 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -2308,7 +2308,7 @@ mod pallet { impl CourtPalletApi for Pallet where T: Config {} - // No-one can bound more than BalanceOf, therefore, this functions saturates + // No one can own more than `BalanceOf::MAX`, it doesn't matter if this function saturates. pub fn get_appeal_bond(n: usize) -> BalanceOf where T: Config, From 1b7fd2cc1952f4ec106e8c9693eede0e15393999 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 12:39:36 +0200 Subject: [PATCH 310/368] update integrity test --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 78f522167..998e9a49f 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -472,7 +472,7 @@ mod pallet { } fn integrity_test() { - assert!(!T::BlocksPerYear::get().is_zero(), "Blocks per year assumption changed."); + assert!(!T::BlocksPerYear::get().is_zero(), "Blocks per year musn't be zero!"); } } From 59a0644a19bb7349548c99c4627670a2c1e26452 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 12:56:49 +0200 Subject: [PATCH 311/368] improve join court doc --- zrml/court/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 87752334e..8af41ad74 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -480,6 +480,8 @@ mod pallet { impl Pallet { /// Join to become a juror, who is able to get randomly selected /// for court cases according to the provided stake. + /// If the juror is already part of the court, + /// the `amount` needs to be higher than the previous amount to update the juror stake. /// If the juror gets selected for a court case, the juror has to vote and reveal the vote. /// If the juror does not vote or reveal the vote, the juror gets slashed /// by the selected multiple of `MinJurorStake` for the court. @@ -516,6 +518,8 @@ mod pallet { /// Join the court to become a delegator. /// The `amount` of this call represents the total stake of the delegator. + /// If the delegator is already part of the court, + /// the `amount` needs to be higher than the previous amount to update the delegator stake. /// If the random selection algorithm chooses a delegators stake, /// the caller delegates the vote power to the drawn delegated juror. /// The delegator gets slashed or rewarded according to the delegated jurors decisions. From d971286608ffa54086b7f6dc04705b8129168d1e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 13:06:01 +0200 Subject: [PATCH 312/368] improve extrinsic documentation --- zrml/court/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 8af41ad74..406774f49 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -492,7 +492,8 @@ mod pallet { /// Then you risk at most `MinJurorStake` for this court. /// The probability to get selected is higher the more funds are staked. /// The `amount` of this call represents the total stake of the juror. - /// If the pool is full, the lowest staked juror is removed from the juror pool. + /// If the pool is full, the lowest staked court participant is removed from the court pool. + /// If the `amount` is lower than the lowest staked court participant, the call fails. /// /// # Arguments /// @@ -517,12 +518,14 @@ mod pallet { } /// Join the court to become a delegator. - /// The `amount` of this call represents the total stake of the delegator. - /// If the delegator is already part of the court, - /// the `amount` needs to be higher than the previous amount to update the delegator stake. /// If the random selection algorithm chooses a delegators stake, /// the caller delegates the vote power to the drawn delegated juror. /// The delegator gets slashed or rewarded according to the delegated jurors decisions. + /// If the delegator is already part of the court, + /// the `amount` needs to be higher than the previous amount to update the delegators stake. + /// The `amount` of this call represents the total stake of the delegator. + /// If the pool is full, the lowest staked court participant is removed from the court pool. + /// If the `amount` is lower than the lowest staked court participant, the call fails. /// /// # Arguments /// From 593f5e0212d31e2b772473e94cf124724b6bb8b9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 13:14:40 +0200 Subject: [PATCH 313/368] update delegate doc --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 406774f49..7d91a0cb2 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -520,7 +520,7 @@ mod pallet { /// Join the court to become a delegator. /// If the random selection algorithm chooses a delegators stake, /// the caller delegates the vote power to the drawn delegated juror. - /// The delegator gets slashed or rewarded according to the delegated jurors decisions. + /// The delegator gets slashed or rewarded according to the delegated juror's decisions. /// If the delegator is already part of the court, /// the `amount` needs to be higher than the previous amount to update the delegators stake. /// The `amount` of this call represents the total stake of the delegator. From b8deb6766ef376704e9fa54c35b614bf4e82c657 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 14:13:58 +0200 Subject: [PATCH 314/368] make some types package private --- zrml/court/src/types.rs | 61 +++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index df041ebe3..f9ad8f4a2 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -81,31 +81,31 @@ impl VoteItem { pub struct CourtParticipantInfo { /// The court participants amount in the stake weighted pool. /// This amount is used to find a court participant with a binary search on the pool. - pub stake: Balance, + pub(crate) stake: Balance, /// The current amount of funds which are locked in courts. - pub active_lock: Balance, + pub(crate) active_lock: Balance, /// The block number when an exit from court was requested. - pub prepare_exit_at: Option, + pub(crate) prepare_exit_at: Option, /// The delegations of the court participant. This determines the account as a delegator. - pub delegations: Option, + pub(crate) delegations: Option, } /// The raw information behind the secret hash of a juror's vote. -pub struct RawCommitment { +pub(crate) struct RawCommitment { /// The juror's account id. - pub juror: AccountId, + pub(crate) juror: AccountId, /// The vote item which the juror voted for. - pub vote_item: VoteItem, + pub(crate) vote_item: VoteItem, /// The salt which was used to hash the vote. - pub salt: Hash, + pub(crate) salt: Hash, } /// The raw information which is hashed to create the secret hash of a juror's vote. -pub struct CommitmentMatcher { +pub(crate) struct CommitmentMatcher { /// The juror's hashed commitment - pub hashed: Hash, + pub(crate) hashed: Hash, /// The raw commitment which is intended to lead to the hashed commitment. - pub raw: RawCommitment, + pub(crate) raw: RawCommitment, } /// All possible states of a vote. @@ -187,11 +187,11 @@ pub enum CourtStatus { )] pub struct AppealInfo { /// The account which made the appeal. - pub backer: AccountId, + pub(crate) backer: AccountId, /// The amount of funds which were locked for the appeal. - pub bond: Balance, + pub(crate) bond: Balance, /// The vote item which was appealed. - pub appealed_vote_item: VoteItem, + pub(crate) appealed_vote_item: VoteItem, } /// The information about a court case. @@ -217,7 +217,7 @@ pub struct CourtInfo { } /// The timing information about a court case. -pub struct RoundTiming { +pub(crate) struct RoundTiming { /// The end block of the pre-vote period. pub pre_vote_end: BlockNumber, /// The block duration for votes. @@ -231,7 +231,10 @@ pub struct RoundTiming { impl CourtInfo { - pub fn new(round_timing: RoundTiming, vote_item_type: VoteItemType) -> Self { + pub(crate) fn new( + round_timing: RoundTiming, + vote_item_type: VoteItemType, + ) -> Self { let pre_vote = round_timing.pre_vote_end; let vote = pre_vote.saturating_add(round_timing.vote_period); let aggregation = vote.saturating_add(round_timing.aggregation_period); @@ -241,7 +244,7 @@ impl Self { status, appeals: Default::default(), cycle_ends, vote_item_type } } - pub fn update_lifecycle(&mut self, round_timing: RoundTiming) { + pub(crate) fn update_lifecycle(&mut self, round_timing: RoundTiming) { self.cycle_ends.pre_vote = round_timing.pre_vote_end; self.cycle_ends.vote = self.cycle_ends.pre_vote.saturating_add(round_timing.vote_period); self.cycle_ends.aggregation = @@ -309,14 +312,14 @@ pub struct CourtPoolItem { PartialEq, Eq, )] -pub struct SelectionValue { +pub(crate) struct SelectionValue { /// The overall weight of the juror or delegator for a specific selected draw. - pub weight: u32, + pub(crate) weight: u32, /// The amount that can be slashed for this selected draw. - pub slashable: Balance, + pub(crate) slashable: Balance, /// The different portions of stake distributed over multiple jurors. /// The sum of all delegated stakes should be equal to `slashable`. - pub delegated_stakes: DelegatedStakes, + pub(crate) delegated_stakes: DelegatedStakes, } /// The type to add one weight to the selected draws. @@ -330,7 +333,7 @@ pub struct SelectionValue { PartialEq, Eq, )] -pub enum SelectionAdd { +pub(crate) enum SelectionAdd { /// The variant to add an active juror, who is not a delegator. SelfStake { lock: Balance }, /// The variant to decide that a delegator is added @@ -341,22 +344,22 @@ pub enum SelectionAdd { } /// The information about an active juror who voted for a court. -pub struct SelfInfo { +pub(crate) struct SelfInfo { /// The slashable amount of the juror herself. - pub slashable: Balance, + pub(crate) slashable: Balance, /// The item for which the juror voted. - pub vote_item: VoteItem, + pub(crate) vote_item: VoteItem, } -pub struct JurorVoteWithStakes { +pub(crate) struct JurorVoteWithStakes { /// An optional information about an active juror herself, who was selected and voted. /// This could be None, because delegators could have delegated to a juror who failed to vote. - pub self_info: Option>, + pub(crate) self_info: Option>, // many delegators can have delegated to the same juror // that's why the value is a vector and should be sorted (binary search by key) // the key is the delegator account // the value is the delegated stake - pub delegations: Vec<(AccountId, Balance)>, + pub(crate) delegations: Vec<(AccountId, Balance)>, } impl Default for JurorVoteWithStakes { @@ -366,6 +369,6 @@ impl Default for JurorVoteWithStakes { } /// An internal error type to determine how the selection of draws fails. -pub enum SelectionError { +pub(crate) enum SelectionError { NoValidDelegatedJuror, } From d95b2893f92faedb34c34bc9d4a233b188ccd42a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 14:59:42 +0200 Subject: [PATCH 315/368] fix struct comment --- zrml/court/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index f9ad8f4a2..50bc94949 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -275,7 +275,7 @@ pub struct Draw { /// The information about the vote state. pub vote: Vote, /// The amount of funds which can be slashed for this court case. - /// This is related to a multiple of `MinStake` to mitigate Sybil attacks. + /// This is equal to a multiple of `MinJurorStake` to mitigate Sybil attacks. pub slashable: Balance, } From 3ba272279c3612ca900f799af7d29f6ab9a01b05 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 15:27:05 +0200 Subject: [PATCH 316/368] update readme --- zrml/court/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index fde326e9f..5d8a45b9a 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -9,9 +9,8 @@ A pallet for stake-weighted plurality decision making. ## Overview -The court system is responsible for ensuring that the truth is added to the -blockchain. Prediction markets, which depend on accurate information, reward -traders who base their decisions on truthful data. If someone provides false +Court is a market dispute resolution mechanism. It is responsible for ensuring +that the truth is added to the blockchain. If someone provides false information, they will be punished, while those who share accurate information will be rewarded. From 220db6b41fc62d9336b0b6be7d808b5d650ecdb4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 15:31:57 +0200 Subject: [PATCH 317/368] update readme --- zrml/court/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index 5d8a45b9a..4af7b7704 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -9,10 +9,9 @@ A pallet for stake-weighted plurality decision making. ## Overview -Court is a market dispute resolution mechanism. It is responsible for ensuring -that the truth is added to the blockchain. If someone provides false -information, they will be punished, while those who share accurate information -will be rewarded. +Court is a market dispute resolution mechanism. It allows jurors to make decisions. +If a juror does not vote with the plurality of the other jurors, the juror will be punished, +while those who did vote with the plurality will be rewarded. ## Terminology From 8f95fbbff2274dac30f62699e1ca577e935ee07e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 16:49:29 +0200 Subject: [PATCH 318/368] remove commitment matcher --- zrml/court/src/lib.rs | 23 ++++++++++------------- zrml/court/src/types.rs | 8 -------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7d91a0cb2..9507a206c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -25,9 +25,9 @@ extern crate alloc; use crate::{ traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, weights::WeightInfoZeitgeist, - AppealInfo, CommitmentMatcher, CourtId, CourtInfo, CourtParticipantInfo, CourtPoolItem, - CourtStatus, Draw, JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, - SelectionError, SelectionValue, SelfInfo, Vote, VoteItem, VoteItemType, + AppealInfo, CourtId, CourtInfo, CourtParticipantInfo, CourtPoolItem, CourtStatus, Draw, + JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, SelectionError, SelectionValue, + SelfInfo, Vote, VoteItem, VoteItemType, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -244,7 +244,6 @@ mod pallet { pub(crate) type SelectedDrawsOf = BoundedVec, ::MaxSelectedDraws>; pub(crate) type AppealOf = AppealInfo, BalanceOf>; pub(crate) type AppealsOf = BoundedVec, ::MaxAppeals>; - pub(crate) type CommitmentMatcherOf = CommitmentMatcher, HashOf>; pub(crate) type RawCommitmentOf = RawCommitment, HashOf>; pub(crate) type CacheSize = ConstU32<64>; @@ -1951,14 +1950,14 @@ mod pallet { } // Check if the (juror, vote_item, salt) combination matches the secret hash of the vote. - pub(crate) fn is_valid(commitment_matcher: CommitmentMatcherOf) -> DispatchResult { - let CommitmentMatcher { - hashed: commitment, - raw: RawCommitment { juror, vote_item, salt }, - } = commitment_matcher; + pub(crate) fn compare_commitment( + hashed_commitment: T::Hash, + raw_commitment: RawCommitmentOf, + ) -> DispatchResult { + let RawCommitment { juror, vote_item, salt } = raw_commitment; ensure!( - commitment == T::Hashing::hash_of(&(juror, vote_item, salt)), + hashed_commitment == T::Hashing::hash_of(&(juror, vote_item, salt)), Error::::InvalidReveal ); @@ -1974,9 +1973,7 @@ mod pallet { ) -> Result { match vote { Vote::Secret { commitment } => { - let commitment_matcher = - CommitmentMatcher { hashed: commitment, raw: raw_commitment }; - Self::is_valid(commitment_matcher)?; + Self::compare_commitment(commitment, raw_commitment)?; Ok(commitment) } Vote::Drawn => Err(Error::::JurorNotVoted.into()), diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 50bc94949..a0ab344dc 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -100,14 +100,6 @@ pub(crate) struct RawCommitment { pub(crate) salt: Hash, } -/// The raw information which is hashed to create the secret hash of a juror's vote. -pub(crate) struct CommitmentMatcher { - /// The juror's hashed commitment - pub(crate) hashed: Hash, - /// The raw commitment which is intended to lead to the hashed commitment. - pub(crate) raw: RawCommitment, -} - /// All possible states of a vote. #[derive( parity_scale_codec::Decode, From d89ad7a9c47c1bc5e829bcd744c795f14068f3db Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 16:59:05 +0200 Subject: [PATCH 319/368] fix doc string --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 9507a206c..8fc470d0c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -621,7 +621,7 @@ mod pallet { /// # Arguments /// /// - `court_participant`: The court participant, - /// who is assumed to be not be part of the pool anymore. + /// who is assumed not to be part of the pool anymore. /// /// # Weight /// From 0c5b8dd2773aebdb7be4dc3a86f597a053fece04 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 17:07:51 +0200 Subject: [PATCH 320/368] remove unnecessary error --- zrml/court/src/lib.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 8fc470d0c..275dedfdd 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -413,8 +413,6 @@ mod pallet { AmountExceedsBalance, /// After the first join of the court the amount has to be higher than the current stake. AmountBelowLastJoin, - /// The random number generation failed, because the juror total stake is too low. - NotEnoughTotalJurorStakeForRandomNumberGeneration, /// The amount is too low to kick the lowest juror out of the stake-weighted pool. AmountBelowLowestJuror, /// This should not happen, because the juror account should only be once in a pool. @@ -1258,10 +1256,7 @@ mod pallet { let min_juror_stake = T::MinJurorStake::get().saturated_into::(); debug_assert!((max % min_juror_stake).is_zero(), "This is ensured by the caller."); let sections_len = max.checked_div(min_juror_stake).unwrap_or(0); - - if sections_len < (n as u128) { - return Err(Error::::NotEnoughTotalJurorStakeForRandomNumberGeneration.into()); - } + debug_assert!(sections_len >= (n as u128)); let mut swaps = BTreeMap::::new(); let mut random_section_starts = BTreeSet::new(); From 4e1e405b90109a1a19e88fc4f7d1ad7793a2a1f0 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 16 May 2023 17:14:55 +0200 Subject: [PATCH 321/368] improve import --- zrml/court/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 275dedfdd..a6fa60e5c 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -52,7 +52,7 @@ use frame_system::{ ensure_signed, pallet_prelude::{BlockNumberFor, OriginFor}, }; -use rand::{RngCore, SeedableRng}; +use rand::{Rng, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; use sp_arithmetic::{per_things::Perquintill, traits::One}; use sp_runtime::{ @@ -1250,7 +1250,6 @@ mod pallet { n: usize, max: u128, ) -> Result, DispatchError> { - use rand::Rng; let mut rng = Self::rng(); let min_juror_stake = T::MinJurorStake::get().saturated_into::(); From 83b756088f73429e2c17781ecb4ef14bed6f50d3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 17 May 2023 10:32:05 +0200 Subject: [PATCH 322/368] improve test case --- zrml/court/src/tests.rs | 86 ++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index ee6921ace..3440085f9 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -424,7 +424,7 @@ fn prepare_exit_court_works() { } #[test] -fn prepare_exit_court_removes_correct_jurors() { +fn prepare_exit_court_removes_lowest_staked_juror() { ExtBuilder::default().build().execute_with(|| { let min = MinJurorStake::get(); let min_amount = 2 * min; @@ -436,20 +436,78 @@ fn prepare_exit_court_removes_correct_jurors() { assert_ok!(Court::join_court(Origin::signed(juror), amount)); } - for r in 0..CourtPoolOf::::bound() { - let len = CourtPool::::get().into_inner().len(); - assert!( - CourtPool::::get() - .into_inner() - .iter() - .any(|item| item.court_participant == r as u128) - ); - assert_ok!(Court::prepare_exit_court(Origin::signed(r as u128))); - assert_eq!(CourtPool::::get().into_inner().len(), len - 1); - CourtPool::::get().into_inner().iter().for_each(|item| { - assert_ne!(item.court_participant, r as u128); - }); + let len = CourtPool::::get().into_inner().len(); + assert!( + CourtPool::::get() + .into_inner() + .iter() + .any(|item| item.court_participant == 0u128) + ); + assert_ok!(Court::prepare_exit_court(Origin::signed(0u128))); + assert_eq!(CourtPool::::get().into_inner().len(), len - 1); + CourtPool::::get().into_inner().iter().for_each(|item| { + assert_ne!(item.court_participant, 0u128); + }); + }); +} + +#[test] +fn prepare_exit_court_removes_middle_staked_juror() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let min_amount = 2 * min; + + for i in 0..CourtPoolOf::::bound() { + let amount = min_amount + i as u128; + let juror = i as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); + } + + let middle_index = (CourtPoolOf::::bound() / 2) as u128; + + let len = CourtPool::::get().into_inner().len(); + assert!( + CourtPool::::get() + .into_inner() + .iter() + .any(|item| item.court_participant == middle_index) + ); + assert_ok!(Court::prepare_exit_court(Origin::signed(middle_index))); + assert_eq!(CourtPool::::get().into_inner().len(), len - 1); + CourtPool::::get().into_inner().iter().for_each(|item| { + assert_ne!(item.court_participant, middle_index); + }); + }); +} + +#[test] +fn prepare_exit_court_removes_highest_staked_juror() { + ExtBuilder::default().build().execute_with(|| { + let min = MinJurorStake::get(); + let min_amount = 2 * min; + + for i in 0..CourtPoolOf::::bound() { + let amount = min_amount + i as u128; + let juror = i as u128; + let _ = Balances::deposit(&juror, amount).unwrap(); + assert_ok!(Court::join_court(Origin::signed(juror), amount)); } + + let last_index = (CourtPoolOf::::bound() - 1) as u128; + + let len = CourtPool::::get().into_inner().len(); + assert!( + CourtPool::::get() + .into_inner() + .iter() + .any(|item| item.court_participant == last_index) + ); + assert_ok!(Court::prepare_exit_court(Origin::signed(last_index))); + assert_eq!(CourtPool::::get().into_inner().len(), len - 1); + CourtPool::::get().into_inner().iter().for_each(|item| { + assert_ne!(item.court_participant, last_index); + }); }); } From ad0c472a7826e21477191cd3bd89e4ddb95ab6a4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 17 May 2023 12:59:32 +0200 Subject: [PATCH 323/368] fix inflation misbehaviour --- zrml/court/src/benchmarks.rs | 3 +- zrml/court/src/lib.rs | 32 ++++++--- zrml/court/src/tests.rs | 127 +++++++++++++++++++++++++++++++---- zrml/court/src/types.rs | 4 +- 4 files changed, 144 insertions(+), 22 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 082a931a1..48d3fac02 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -96,6 +96,7 @@ where let mut pool = >::get(); let min_amount = T::MinJurorStake::get(); let max_amount = min_amount + min_amount + BalanceOf::::from(number); + let joined_at = >::block_number(); for i in 0..number { let juror: T::AccountId = account("juror", i, 0); let stake = max_amount - BalanceOf::::from(i); @@ -110,7 +111,7 @@ where }, ); let consumed_stake = BalanceOf::::zero(); - let pool_item = CourtPoolItem { stake, court_participant: juror.clone(), consumed_stake }; + let pool_item = CourtPoolItem { stake, court_participant: juror.clone(), consumed_stake, joined_at }; match pool.binary_search_by_key(&(stake, &juror), |pool_item| { (pool_item.stake, &pool_item.court_participant) }) { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index a6fa60e5c..b5045d004 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -237,7 +237,8 @@ mod pallet { pub(crate) type JurorVoteWithStakesOf = JurorVoteWithStakes, BalanceOf>; pub(crate) type CourtParticipantInfoOf = CourtParticipantInfo, BlockNumberFor, DelegationsOf>; - pub(crate) type CourtPoolItemOf = CourtPoolItem, BalanceOf>; + pub(crate) type CourtPoolItemOf = + CourtPoolItem, BalanceOf, BlockNumberFor>; pub(crate) type CourtPoolOf = BoundedVec, ::MaxCourtParticipants>; pub(crate) type DrawOf = Draw, BalanceOf, HashOf, DelegatedStakesOf>; @@ -1116,7 +1117,9 @@ mod pallet { let mut pool = CourtPool::::get(); - let (active_lock, consumed_stake) = if let Some(prev_p_info) = + let now = >::block_number(); + + let (active_lock, consumed_stake, joined_at) = if let Some(prev_p_info) = >::get(who) { ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); @@ -1127,8 +1130,9 @@ mod pallet { "If the pool item is found, the prepare_exit_at could have never been written." ); let consumed_stake = pool_item.consumed_stake; + let joined_at = pool_item.joined_at; pool.remove(index); - (prev_p_info.active_lock, consumed_stake) + (prev_p_info.active_lock, consumed_stake, joined_at) } else { if pool.is_full() { let lowest_item = pool.first(); @@ -1150,7 +1154,8 @@ mod pallet { // remove the lowest staked court participant pool.remove(0); } - (>::zero(), >::zero()) + + (>::zero(), >::zero(), now) }; match pool.binary_search_by_key(&(amount, who), |pool_item| { @@ -1171,6 +1176,7 @@ mod pallet { stake: amount, court_participant: who.clone(), consumed_stake, + joined_at, }, ) .map_err(|_| { @@ -1217,10 +1223,20 @@ mod pallet { let pool = >::get(); let pool_len = pool.len() as u32; - let total_stake = pool.iter().fold(0u128, |acc, pool_item| { - acc.saturating_add(pool_item.stake.saturated_into::()) - }); - for CourtPoolItem { stake, court_participant, .. } in pool { + let at_least_one_inflation_period = + |joined_at| now.saturating_sub(joined_at) >= T::InflationPeriod::get(); + let total_stake = pool + .iter() + .filter(|pool_item| at_least_one_inflation_period(pool_item.joined_at)) + .fold(0u128, |acc, pool_item| { + acc.saturating_add(pool_item.stake.saturated_into::()) + }); + for CourtPoolItem { stake, court_participant, joined_at, .. } in pool { + if !at_least_one_inflation_period(joined_at) { + // participants who joined and didn't wait + // at least one full inflation period won't get a reward + continue; + } let share = Perquintill::from_rational(stake.saturated_into::(), total_stake); let mint = share * inflation_period_mint.saturated_into::(); diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 3440085f9..9ab8624c5 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -192,6 +192,7 @@ fn join_court_successfully_stores_required_data() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; let alice_free_balance_before = Balances::free_balance(ALICE); + let joined_at = >::block_number(); assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); System::assert_last_event(Event::JurorJoined { juror: ALICE, stake: amount }.into()); assert_eq!( @@ -210,7 +211,12 @@ fn join_court_successfully_stores_required_data() { assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_eq!( CourtPool::::get().into_inner(), - vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] + vec![CourtPoolItem { + stake: amount, + court_participant: ALICE, + consumed_stake: 0, + joined_at + }] ); }); } @@ -220,11 +226,17 @@ fn join_court_works_multiple_joins() { ExtBuilder::default().build().execute_with(|| { let min = MinJurorStake::get(); let amount = 2 * min; + let joined_at_0 = >::block_number(); assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!(Balances::locks(ALICE), vec![the_lock(amount)]); assert_eq!( CourtPool::::get().into_inner(), - vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] + vec![CourtPoolItem { + stake: amount, + court_participant: ALICE, + consumed_stake: 0, + joined_at: joined_at_0 + }] ); assert_eq!( Participants::::iter() @@ -240,13 +252,24 @@ fn join_court_works_multiple_joins() { )] ); + let joined_at_1 = >::block_number(); assert_ok!(Court::join_court(Origin::signed(BOB), amount)); assert_eq!(Balances::locks(BOB), vec![the_lock(amount)]); assert_eq!( CourtPool::::get().into_inner(), vec![ - CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }, - CourtPoolItem { stake: amount, court_participant: BOB, consumed_stake: 0 } + CourtPoolItem { + stake: amount, + court_participant: ALICE, + consumed_stake: 0, + joined_at: joined_at_0 + }, + CourtPoolItem { + stake: amount, + court_participant: BOB, + consumed_stake: 0, + joined_at: joined_at_1 + } ] ); assert_eq!(Participants::::iter().count(), 2); @@ -276,8 +299,18 @@ fn join_court_works_multiple_joins() { assert_eq!( CourtPool::::get().into_inner(), vec![ - CourtPoolItem { stake: amount, court_participant: BOB, consumed_stake: 0 }, - CourtPoolItem { stake: higher_amount, court_participant: ALICE, consumed_stake: 0 }, + CourtPoolItem { + stake: amount, + court_participant: BOB, + consumed_stake: 0, + joined_at: joined_at_1 + }, + CourtPoolItem { + stake: higher_amount, + court_participant: ALICE, + consumed_stake: 0, + joined_at: joined_at_0 + }, ] ); assert_eq!(Participants::::iter().count(), 2); @@ -319,8 +352,13 @@ fn join_court_saves_consumed_stake_and_active_lock_for_double_join() { delegations: Default::default(), }, ); - let juror_pool = - vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake }]; + let joined_at = >::block_number(); + let juror_pool = vec![CourtPoolItem { + stake: amount, + court_participant: ALICE, + consumed_stake, + joined_at, + }]; CourtPool::::put::>(juror_pool.try_into().unwrap()); let higher_amount = amount + 1; @@ -411,10 +449,16 @@ fn join_court_fails_amount_below_lowest_juror() { fn prepare_exit_court_works() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; + let joined_at = >::block_number(); assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!( CourtPool::::get().into_inner(), - vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] + vec![CourtPoolItem { + stake: amount, + court_participant: ALICE, + consumed_stake: 0, + joined_at + }] ); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); @@ -541,10 +585,16 @@ fn join_court_binary_search_sorted_insert_works() { fn prepare_exit_court_fails_juror_already_prepared_to_exit() { ExtBuilder::default().build().execute_with(|| { let amount = 2 * BASE; + let joined_at = >::block_number(); assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_eq!( CourtPool::::get().into_inner(), - vec![CourtPoolItem { stake: amount, court_participant: ALICE, consumed_stake: 0 }] + vec![CourtPoolItem { + stake: amount, + court_participant: ALICE, + consumed_stake: 0, + joined_at + }] ); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); @@ -2955,19 +3005,26 @@ fn handle_inflation_works() { let mut jurors = >::get(); let mut free_balances_before = BTreeMap::new(); let jurors_list = vec![1000, 10_000, 100_000, 1_000_000, 10_000_000]; + run_to_block(InflationPeriod::get()); + let joined_at = >::block_number(); for number in jurors_list.iter() { let stake = *number; let juror = *number; let _ = Balances::deposit(&juror, stake).unwrap(); free_balances_before.insert(juror, stake); jurors - .try_push(CourtPoolItem { stake, court_participant: juror, consumed_stake: 0 }) + .try_push(CourtPoolItem { + stake, + court_participant: juror, + consumed_stake: 0, + joined_at, + }) .unwrap(); } >::put(jurors.clone()); let inflation_period = InflationPeriod::get(); - run_to_block(inflation_period); + run_blocks(inflation_period); let now = >::block_number(); Court::handle_inflation(now); @@ -2987,3 +3044,49 @@ fn handle_inflation_works() { assert_eq!(free_balance_after_4 - free_balances_before[&jurors_list[4]], 432_868_408_838); }); } + +#[test] +fn handle_inflation_without_waiting_one_inflation_period() { + ExtBuilder::default().build().execute_with(|| { + let mut jurors = >::get(); + let mut free_balances_before = BTreeMap::new(); + let jurors_list = vec![1000, 10_000, 100_000, 1_000_000, 10_000_000]; + run_to_block(InflationPeriod::get()); + let joined_at = >::block_number(); + for number in jurors_list.iter() { + let stake = *number; + let juror = *number; + let _ = Balances::deposit(&juror, stake).unwrap(); + free_balances_before.insert(juror, stake); + jurors + .try_push(CourtPoolItem { + stake, + court_participant: juror, + consumed_stake: 0, + joined_at, + }) + .unwrap(); + } + >::put(jurors.clone()); + + let inflation_period = InflationPeriod::get(); + run_blocks(inflation_period.saturating_sub(1)); + let now = >::block_number(); + Court::handle_inflation(now); + + let free_balance_after_0 = Balances::free_balance(jurors_list[0]); + assert_eq!(free_balance_after_0 - free_balances_before[&jurors_list[0]], 0); + + let free_balance_after_1 = Balances::free_balance(jurors_list[1]); + assert_eq!(free_balance_after_1 - free_balances_before[&jurors_list[1]], 0); + + let free_balance_after_2 = Balances::free_balance(jurors_list[2]); + assert_eq!(free_balance_after_2 - free_balances_before[&jurors_list[2]], 0); + + let free_balance_after_3 = Balances::free_balance(jurors_list[3]); + assert_eq!(free_balance_after_3 - free_balances_before[&jurors_list[3]], 0); + + let free_balance_after_4 = Balances::free_balance(jurors_list[4]); + assert_eq!(free_balance_after_4 - free_balances_before[&jurors_list[4]], 0); + }); +} diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index a0ab344dc..3c3d128a0 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -282,7 +282,7 @@ pub struct Draw { PartialEq, Eq, )] -pub struct CourtPoolItem { +pub struct CourtPoolItem { /// The amount of funds associated to a court participant /// in order to get selected for a court case. pub stake: Balance, @@ -291,6 +291,8 @@ pub struct CourtPoolItem { /// The consumed amount of the stake for all draws. This is useful to reduce the probability /// of a court participant to be selected again. pub consumed_stake: Balance, + /// The block number at which the participant joined. + pub joined_at: BlockNumber, } /// The information about an internal selected draw of a juror or delegator. From 1fd7d28c1d831187c52786ce3a3cf0f5c86313e6 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 19 May 2023 11:19:08 +0200 Subject: [PATCH 324/368] improve round timing structure --- zrml/court/src/benchmarks.rs | 15 ++--- zrml/court/src/lib.rs | 38 ++++++------ zrml/court/src/tests.rs | 40 ++++++------- zrml/court/src/types.rs | 87 ++++++++++++---------------- zrml/prediction-markets/src/tests.rs | 12 ++-- 5 files changed, 91 insertions(+), 101 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 48d3fac02..9e2711322 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -111,7 +111,8 @@ where }, ); let consumed_stake = BalanceOf::::zero(); - let pool_item = CourtPoolItem { stake, court_participant: juror.clone(), consumed_stake, joined_at }; + let pool_item = + CourtPoolItem { stake, court_participant: juror.clone(), consumed_stake, joined_at }; match pool.binary_search_by_key(&(stake, &juror), |pool_item| { (pool_item.stake, &pool_item.court_participant) }) { @@ -308,7 +309,7 @@ benchmarks! { let (market_id, court_id) = setup_court::()?; let court = >::get(court_id).unwrap(); - let pre_vote = court.cycle_ends.pre_vote; + let pre_vote = court.round_ends.pre_vote; fill_draws::(court_id, d)?; @@ -340,7 +341,7 @@ benchmarks! { let (market_id, court_id) = setup_court::()?; let court = >::get(court_id).unwrap(); - let pre_vote = court.cycle_ends.pre_vote; + let pre_vote = court.round_ends.pre_vote; fill_draws::(court_id, d)?; @@ -383,7 +384,7 @@ benchmarks! { let (market_id, court_id) = setup_court::()?; let court = >::get(court_id).unwrap(); - let vote_end = court.cycle_ends.vote; + let vote_end = court.round_ends.vote; fill_draws::(court_id, d)?; @@ -431,14 +432,14 @@ benchmarks! { let (market_id, court_id) = setup_court::()?; let mut court = >::get(court_id).unwrap(); - let appeal_end = court.cycle_ends.appeal; + let appeal_end = court.round_ends.appeal; for i in 0..r { let market_id_i = (i + 100).saturated_into::>(); T::DisputeResolution::add_auto_resolve(&market_id_i, appeal_end).unwrap(); } T::DisputeResolution::add_auto_resolve(&market_id, appeal_end).unwrap(); - let aggregation = court.cycle_ends.aggregation; + let aggregation = court.round_ends.aggregation; for i in 0..a { let appeal_info = AppealInfo { backer: account("backer", i, 0), @@ -490,7 +491,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller), court_id) verify { let court = >::get(court_id).unwrap(); - assert_eq!(court.cycle_ends.appeal, new_resolve_at); + assert_eq!(court.round_ends.appeal, new_resolve_at); } reassign_court_stakes { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index b5045d004..7d12adf4a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -693,7 +693,7 @@ mod pallet { let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; let now = >::block_number(); ensure!( - court.cycle_ends.pre_vote < now && now <= court.cycle_ends.vote, + court.round_ends.pre_vote < now && now <= court.round_ends.vote, Error::::NotInVotingPeriod ); @@ -772,7 +772,7 @@ mod pallet { let now = >::block_number(); // ensure in vote period ensure!( - court.cycle_ends.pre_vote < now && now <= court.cycle_ends.vote, + court.round_ends.pre_vote < now && now <= court.round_ends.vote, Error::::NotInVotingPeriod ); @@ -841,7 +841,7 @@ mod pallet { let now = >::block_number(); ensure!( - court.cycle_ends.vote < now && now <= court.cycle_ends.aggregation, + court.round_ends.vote < now && now <= court.round_ends.aggregation, Error::::NotInAggregationPeriod ); @@ -916,7 +916,7 @@ mod pallet { Error::::MaxAppealsReached })?; - let last_resolve_at = court.cycle_ends.appeal; + let last_resolve_at = court.round_ends.appeal; // used for benchmarking, juror pool is queried inside `select_jurors` let pool_len = >::decode_len().unwrap_or(0) as u32; @@ -928,14 +928,14 @@ mod pallet { let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { - pre_vote_end: request_block, - vote_period: T::VotePeriod::get(), - aggregation_period: T::AggregationPeriod::get(), - appeal_period: T::AppealPeriod::get(), + pre_vote: request_block, + vote: T::VotePeriod::get(), + aggregation: T::AggregationPeriod::get(), + appeal: T::AppealPeriod::get(), }; - // sets cycle_ends one after the other from now - court.update_lifecycle(round_timing); - let new_resolve_at = court.cycle_ends.appeal; + // sets round ends one after the other from now + court.update_round(round_timing); + let new_resolve_at = court.round_ends.appeal; debug_assert!(new_resolve_at != last_resolve_at); if let Some(market_id) = >::get(court_id) { ids_len_1 = T::DisputeResolution::add_auto_resolve(&market_id, new_resolve_at)?; @@ -1719,7 +1719,7 @@ mod pallet { } ensure!( - court.cycle_ends.aggregation < now && now < court.cycle_ends.appeal, + court.round_ends.aggregation < now && now < court.round_ends.appeal, Error::::NotInAppealPeriod ); @@ -2064,18 +2064,18 @@ mod pallet { let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { - pre_vote_end: request_block, - vote_period: T::VotePeriod::get(), - aggregation_period: T::AggregationPeriod::get(), - appeal_period: T::AppealPeriod::get(), + pre_vote: request_block, + vote: T::VotePeriod::get(), + aggregation: T::AggregationPeriod::get(), + appeal: T::AppealPeriod::get(), }; let vote_item_type = VoteItemType::Outcome; - // sets cycle_ends one after the other from now + // sets round ends one after the other from now let court = CourtInfo::new(round_timing, vote_item_type); let ids_len = - T::DisputeResolution::add_auto_resolve(market_id, court.cycle_ends.appeal)?; + T::DisputeResolution::add_auto_resolve(market_id, court.round_ends.appeal)?; >::insert(court_id, new_draws); >::insert(court_id, court); @@ -2171,7 +2171,7 @@ mod pallet { } if let Some(court_id) = >::get(market_id) { - res.result = >::get(court_id).map(|court| court.cycle_ends.appeal); + res.result = >::get(court_id).map(|court| court.round_ends.appeal); } res diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 9ab8624c5..9a84e0ce1 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -1408,7 +1408,7 @@ fn denounce_vote_fails_if_vote_already_denounced() { } #[test] -fn appeal_updates_cycle_ends() { +fn appeal_updates_round_ends() { ExtBuilder::default().build().execute_with(|| { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); @@ -1424,21 +1424,21 @@ fn appeal_updates_cycle_ends() { let request_block = >::get(); assert!(now < request_block); - assert_eq!(court.cycle_ends.pre_vote, request_block); - assert_eq!(court.cycle_ends.vote, request_block + VotePeriod::get()); + assert_eq!(court.round_ends.pre_vote, request_block); + assert_eq!(court.round_ends.vote, request_block + VotePeriod::get()); assert_eq!( - court.cycle_ends.aggregation, + court.round_ends.aggregation, request_block + VotePeriod::get() + AggregationPeriod::get() ); assert_eq!( - court.cycle_ends.appeal, + court.round_ends.appeal, request_block + VotePeriod::get() + AggregationPeriod::get() + AppealPeriod::get() ); - assert!(last_court.cycle_ends.pre_vote < court.cycle_ends.pre_vote); - assert!(last_court.cycle_ends.vote < court.cycle_ends.vote); - assert!(last_court.cycle_ends.aggregation < court.cycle_ends.aggregation); - assert!(last_court.cycle_ends.appeal < court.cycle_ends.appeal); + assert!(last_court.round_ends.pre_vote < court.round_ends.pre_vote); + assert!(last_court.round_ends.vote < court.round_ends.vote); + assert!(last_court.round_ends.aggregation < court.round_ends.aggregation); + assert!(last_court.round_ends.appeal < court.round_ends.appeal); }); } @@ -1481,14 +1481,14 @@ fn appeal_shifts_auto_resolve() { let outcome = OutcomeReport::Scalar(42u128); let (court_id, _, _) = set_alice_after_vote(outcome); - let resolve_at_0 = >::get(court_id).unwrap().cycle_ends.appeal; + let resolve_at_0 = >::get(court_id).unwrap().round_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![0]); run_blocks(VotePeriod::get() + AggregationPeriod::get() + 1); assert_ok!(Court::appeal(Origin::signed(CHARLIE), court_id)); - let resolve_at_1 = >::get(court_id).unwrap().cycle_ends.appeal; + let resolve_at_1 = >::get(court_id).unwrap().round_ends.appeal; assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_1), vec![0]); assert_ne!(resolve_at_0, resolve_at_1); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at_0), vec![]); @@ -1700,7 +1700,7 @@ fn appeal_last_appeal_just_removes_auto_resolve() { fill_appeals(court_id, (MaxAppeals::get() - 1) as usize); let court = >::get(court_id).unwrap(); - let resolve_at = court.cycle_ends.appeal; + let resolve_at = court.round_ends.appeal; let market_id = >::get(court_id).unwrap(); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); @@ -2496,7 +2496,7 @@ fn get_auto_resolve_works() { let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let court = >::get(court_id).unwrap(); - let appeal_end = court.cycle_ends.appeal; + let appeal_end = court.round_ends.appeal; assert_eq!(Court::get_auto_resolve(&market_id, &market).result, Some(appeal_end)); }); } @@ -2692,12 +2692,12 @@ fn on_dispute_creates_correct_court_info() { ExtBuilder::default().build().execute_with(|| { let court_id = initialize_court(); let court = >::get(court_id).unwrap(); - let cycle_ends = court.cycle_ends; + let round_ends = court.round_ends; let request_block = >::get(); - assert_eq!(cycle_ends.pre_vote, request_block); - assert_eq!(cycle_ends.vote, cycle_ends.pre_vote + VotePeriod::get()); - assert_eq!(cycle_ends.aggregation, cycle_ends.vote + AggregationPeriod::get()); - assert_eq!(cycle_ends.appeal, cycle_ends.aggregation + AppealPeriod::get()); + assert_eq!(round_ends.pre_vote, request_block); + assert_eq!(round_ends.vote, round_ends.pre_vote + VotePeriod::get()); + assert_eq!(round_ends.aggregation, round_ends.vote + AggregationPeriod::get()); + assert_eq!(round_ends.appeal, round_ends.aggregation + AppealPeriod::get()); assert_eq!(court.status, CourtStatus::Open); assert!(court.appeals.is_empty()); }); @@ -2762,7 +2762,7 @@ fn on_dispute_adds_auto_resolve() { ExtBuilder::default().build().execute_with(|| { let court_id = initialize_court(); let court = >::get(court_id).unwrap(); - let resolve_at = court.cycle_ends.appeal; + let resolve_at = court.round_ends.appeal; let market_id = >::get(court_id).unwrap(); assert_eq!(MarketIdsPerDisputeBlock::::get(resolve_at), vec![market_id]); }); @@ -2777,7 +2777,7 @@ fn has_failed_returns_true_for_appealable_court_too_few_jurors() { let market_id = >::get(court_id).unwrap(); let market = MarketCommons::market(&market_id).unwrap(); let court = >::get(court_id).unwrap(); - let aggregation = court.cycle_ends.aggregation; + let aggregation = court.round_ends.aggregation; run_to_block(aggregation + 1); assert!(Court::has_failed(&market_id, &market).unwrap().result); }); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 3c3d128a0..017572274 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -124,28 +124,6 @@ pub enum Vote { Denounced { commitment: Hash, vote_item: VoteItem, salt: Hash }, } -/// The information about the lifecycle of a court case. -#[derive( - parity_scale_codec::Decode, - parity_scale_codec::Encode, - parity_scale_codec::MaxEncodedLen, - scale_info::TypeInfo, - Clone, - Debug, - PartialEq, - Eq, -)] -pub struct CycleEnds { - /// The end block of the pre-vote period. - pub pre_vote: BlockNumber, - /// The end block of the vote period. - pub vote: BlockNumber, - /// The end block of the aggregation period. - pub aggregation: BlockNumber, - /// The end block of the appeal period. - pub appeal: BlockNumber, -} - /// The status of a court case. #[derive( parity_scale_codec::Decode, @@ -186,6 +164,31 @@ pub struct AppealInfo { pub(crate) appealed_vote_item: VoteItem, } +/// The timing information about a court round. +#[derive( + parity_scale_codec::Decode, + parity_scale_codec::Encode, + parity_scale_codec::MaxEncodedLen, + scale_info::TypeInfo, + Clone, + Debug, + PartialEq, + Eq, +)] +pub struct RoundTiming { + /// The end block of the pre-vote period. + pub pre_vote: BlockNumber, + /// The end block of the vote period. + /// Note this can also be used as the block duration for votes. + pub vote: BlockNumber, + /// The end block of the aggregation period. + /// Note this can also be used as the block duration for revealing votes. + pub aggregation: BlockNumber, + /// The end block of the appeal period. + /// Note this can also be used as the block duration for appeals. + pub appeal: BlockNumber, +} + /// The information about a court case. #[derive( parity_scale_codec::Decode, @@ -202,24 +205,12 @@ pub struct CourtInfo { pub status: CourtStatus, /// The list of all appeals. pub appeals: Appeals, - /// The information about the lifecycle of this court case. - pub cycle_ends: CycleEnds, + /// This specifies the end blocks of the court case phases. + pub round_ends: RoundTiming, /// The type of the vote item. pub vote_item_type: VoteItemType, } -/// The timing information about a court case. -pub(crate) struct RoundTiming { - /// The end block of the pre-vote period. - pub pre_vote_end: BlockNumber, - /// The block duration for votes. - pub vote_period: BlockNumber, - /// The block duration for revealing votes. - pub aggregation_period: BlockNumber, - /// The block duration for appeals. - pub appeal_period: BlockNumber, -} - impl CourtInfo { @@ -227,22 +218,20 @@ impl round_timing: RoundTiming, vote_item_type: VoteItemType, ) -> Self { - let pre_vote = round_timing.pre_vote_end; - let vote = pre_vote.saturating_add(round_timing.vote_period); - let aggregation = vote.saturating_add(round_timing.aggregation_period); - let appeal = aggregation.saturating_add(round_timing.appeal_period); - let cycle_ends = CycleEnds { pre_vote, vote, aggregation, appeal }; + let pre_vote = round_timing.pre_vote; + let vote = pre_vote.saturating_add(round_timing.vote); + let aggregation = vote.saturating_add(round_timing.aggregation); + let appeal = aggregation.saturating_add(round_timing.appeal); + let round_ends = RoundTiming { pre_vote, vote, aggregation, appeal }; let status = CourtStatus::Open; - Self { status, appeals: Default::default(), cycle_ends, vote_item_type } + Self { status, appeals: Default::default(), round_ends, vote_item_type } } - pub(crate) fn update_lifecycle(&mut self, round_timing: RoundTiming) { - self.cycle_ends.pre_vote = round_timing.pre_vote_end; - self.cycle_ends.vote = self.cycle_ends.pre_vote.saturating_add(round_timing.vote_period); - self.cycle_ends.aggregation = - self.cycle_ends.vote.saturating_add(round_timing.aggregation_period); - self.cycle_ends.appeal = - self.cycle_ends.aggregation.saturating_add(round_timing.appeal_period); + pub(crate) fn update_round(&mut self, round_timing: RoundTiming) { + self.round_ends.pre_vote = round_timing.pre_vote; + self.round_ends.vote = self.round_ends.pre_vote.saturating_add(round_timing.vote); + self.round_ends.aggregation = self.round_ends.vote.saturating_add(round_timing.aggregation); + self.round_ends.appeal = self.round_ends.aggregation.saturating_add(round_timing.appeal); } } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 3bcb2c15f..27f7e45f1 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3162,7 +3162,7 @@ fn it_resolves_a_disputed_court_market() { assert_ok!(PredictionMarkets::dispute(Origin::signed(CHARLIE), market_id,)); let court = zrml_court::Courts::::get(market_id).unwrap(); - let vote_start = court.cycle_ends.pre_vote + 1; + let vote_start = court.round_ends.pre_vote + 1; run_to_block(vote_start); @@ -3220,7 +3220,7 @@ fn it_resolves_a_disputed_court_market() { salt )); - let aggregation_start = court.cycle_ends.vote + 1; + let aggregation_start = court.round_ends.vote + 1; run_to_block(aggregation_start); assert_ok!(Court::reveal_vote( @@ -3255,7 +3255,7 @@ fn it_resolves_a_disputed_court_market() { // juror_5 fails to reveal in time - let resolve_at = court.cycle_ends.appeal; + let resolve_at = court.round_ends.appeal; let market_ids = MarketIdsPerDisputeBlock::::get(resolve_at); assert_eq!(market_ids.len(), 1); @@ -3326,7 +3326,7 @@ fn it_resolves_a_disputed_court_market() { fn simulate_appeal_cycle(market_id: MarketId) { let court = zrml_court::Courts::::get(market_id).unwrap(); - let vote_start = court.cycle_ends.pre_vote + 1; + let vote_start = court.round_ends.pre_vote + 1; run_to_block(vote_start); @@ -3342,7 +3342,7 @@ fn simulate_appeal_cycle(market_id: MarketId) { assert_ok!(Court::vote(Origin::signed(draw.court_participant), market_id, commitment)); } - let aggregation_start = court.cycle_ends.vote + 1; + let aggregation_start = court.round_ends.vote + 1; run_to_block(aggregation_start); for draw in draws { @@ -3354,7 +3354,7 @@ fn simulate_appeal_cycle(market_id: MarketId) { )); } - let resolve_at = court.cycle_ends.appeal; + let resolve_at = court.round_ends.appeal; let market_ids = MarketIdsPerDisputeBlock::::get(resolve_at); assert_eq!(market_ids.len(), 1); From 9a7d463dbe42fde1d86ef3cc8129308bc2c143ac Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 19 May 2023 12:10:11 +0200 Subject: [PATCH 325/368] improve error names --- zrml/court/src/lib.rs | 36 +++++++++++++++------------- zrml/court/src/tests.rs | 20 ++++++++-------- zrml/prediction-markets/src/tests.rs | 4 ++-- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7d12adf4a..77fd4dca1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -365,12 +365,12 @@ mod pallet { MarketDoesNotHaveCourtMechanism, /// The market is not in a state where it can be disputed. MarketIsNotDisputed, - /// Only jurors can reveal their votes. - OnlyJurorsCanReveal, + /// This operation requires the caller to be a juror or delegator. + CallerIsNotACourtParticipant, /// The vote is not commitment. VoteAlreadyRevealed, /// The vote item and salt reveal do not match the commitment vote. - InvalidReveal, + CommitmentHashMismatch, /// No court for this market id was found. CourtNotFound, /// This operation is only allowed in the voting period. @@ -390,16 +390,17 @@ mod pallet { /// In order to exit the court the juror has to exit /// the pool first with `prepare_exit_court`. AlreadyPreparedExit, + /// The juror or delegator is not part of the court pool. /// The court participant needs to exit the court and then rejoin. - NeedToExit, + ParticipantNotInCourtPool, /// The juror was not randomly selected for the court. JurorNotDrawn, /// The juror was drawn but did not manage to commitmently vote within the court. - JurorNotVoted, + JurorDidNotVote, /// The juror was already denounced. VoteAlreadyDenounced, /// A juror tried to denounce herself. - SelfDenounceDisallowed, + CallerDenouncedItself, /// The court is not in the closed state. CourtNotClosed, /// The juror stakes of the court already got reassigned. @@ -424,7 +425,7 @@ mod pallet { AppealBondExceedsBalance, /// The juror should at least wait one inflation period after the funds can be unstaked. /// Otherwise hopping in and out for inflation rewards is possible. - WaitFullInflationPeriod, + PrematureExit, /// The `prepare_exit_at` field is not present. PrepareExitAtNotPresent, /// The maximum number of delegations is reached for this account. @@ -448,7 +449,7 @@ mod pallet { /// The vote item is not valid for this (binary) court. InvalidVoteItemForBinaryCourt, /// The appealed vote item is not an outcome. - AppealedVoteItemIsNotOutcome, + AppealedVoteItemIsNoOutcome, /// The winner vote item is not an outcome. WinnerVoteItemIsNoOutcome, /// The outcome does not match the market outcomes. @@ -643,7 +644,7 @@ mod pallet { prev_p_info.prepare_exit_at.ok_or(Error::::PrepareExitAtNotPresent)?; ensure!( now.saturating_sub(prepare_exit_at) >= T::InflationPeriod::get(), - Error::::WaitFullInflationPeriod + Error::::PrematureExit ); let (exit_amount, active_lock, weight) = if prev_p_info.active_lock.is_zero() { @@ -762,7 +763,7 @@ mod pallet { let juror = T::Lookup::lookup(juror)?; - ensure!(denouncer != juror, Error::::SelfDenounceDisallowed); + ensure!(denouncer != juror, Error::::CallerDenouncedItself); ensure!(>::contains_key(&juror), Error::::JurorDoesNotExist); @@ -835,7 +836,10 @@ mod pallet { T::VoteCheck::pre_validate(&market_id, vote_item.clone())?; } - ensure!(>::get(&who).is_some(), Error::::OnlyJurorsCanReveal); + ensure!( + >::get(&who).is_some(), + Error::::CallerIsNotACourtParticipant + ); let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; Self::check_vote_item(&court, &vote_item)?; @@ -859,7 +863,7 @@ mod pallet { Vote::Revealed { commitment, vote_item: vote_item.clone(), salt }; draws[index] = Draw { court_participant: who.clone(), vote: raw_vote, ..draw }; } - Err(_) => return Err(Error::::JurorNotDrawn.into()), + Err(_) => return Err(Error::::CallerNotInSelectedDraws.into()), } let draws_len = draws.len() as u32; @@ -1124,7 +1128,7 @@ mod pallet { { ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); let (index, pool_item) = Self::get_pool_item(&pool, prev_p_info.stake, who) - .ok_or(Error::::NeedToExit)?; + .ok_or(Error::::ParticipantNotInCourtPool)?; debug_assert!( prev_p_info.prepare_exit_at.is_none(), "If the pool item is found, the prepare_exit_at could have never been written." @@ -1968,7 +1972,7 @@ mod pallet { ensure!( hashed_commitment == T::Hashing::hash_of(&(juror, vote_item, salt)), - Error::::InvalidReveal + Error::::CommitmentHashMismatch ); Ok(()) @@ -1986,7 +1990,7 @@ mod pallet { Self::compare_commitment(commitment, raw_commitment)?; Ok(commitment) } - Vote::Drawn => Err(Error::::JurorNotVoted.into()), + Vote::Drawn => Err(Error::::JurorDidNotVote.into()), Vote::Delegated { delegated_stakes: _ } => Err(Error::::JurorDelegated.into()), Vote::Revealed { commitment: _, vote_item: _, salt: _ } => { Err(Error::::VoteAlreadyRevealed.into()) @@ -2140,7 +2144,7 @@ mod pallet { let appealed_vote_item_as_outcome = appealed_vote_item .clone() .into_outcome() - .ok_or(Error::::AppealedVoteItemIsNotOutcome)?; + .ok_or(Error::::AppealedVoteItemIsNoOutcome)?; if resolved_outcome == &appealed_vote_item_as_outcome { let (imb, missing) = T::Currency::slash_reserved_named(&Self::reserve_id(), backer, *bond); diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 9a84e0ce1..fcf000657 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -417,7 +417,7 @@ fn join_court_fails_juror_needs_to_exit() { assert_noop!( Court::join_court(Origin::signed(ALICE), amount + 1), - Error::::NeedToExit + Error::::ParticipantNotInCourtPool ); }); } @@ -741,7 +741,7 @@ fn exit_court_fails_if_inflation_period_not_over() { assert_noop!( Court::exit_court(Origin::signed(ALICE), ALICE), - Error::::WaitFullInflationPeriod + Error::::PrematureExit ); }); } @@ -1021,7 +1021,7 @@ fn reveal_vote_fails_if_caller_not_juror() { assert_noop!( Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), - Error::::OnlyJurorsCanReveal + Error::::CallerIsNotACourtParticipant ); }); } @@ -1077,7 +1077,7 @@ fn reveal_vote_fails_if_juror_not_drawn() { assert_noop!( Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), - Error::::JurorNotDrawn + Error::::CallerNotInSelectedDraws ); }); } @@ -1094,7 +1094,7 @@ fn reveal_vote_fails_for_invalid_reveal() { let invalid_vote_item = VoteItem::Outcome(invalid_outcome); assert_noop!( Court::reveal_vote(Origin::signed(ALICE), court_id, invalid_vote_item, salt), - Error::::InvalidReveal + Error::::CommitmentHashMismatch ); }); } @@ -1113,7 +1113,7 @@ fn reveal_vote_fails_for_invalid_salt() { let vote_item = VoteItem::Outcome(outcome); assert_noop!( Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, incorrect_salt), - Error::::InvalidReveal + Error::::CommitmentHashMismatch ); }); } @@ -1138,7 +1138,7 @@ fn reveal_vote_fails_if_juror_not_voted() { assert_noop!( Court::reveal_vote(Origin::signed(ALICE), court_id, vote_item, salt), - Error::::JurorNotVoted + Error::::JurorDidNotVote ); }); } @@ -1252,7 +1252,7 @@ fn denounce_vote_fails_if_self_denounce() { assert_noop!( Court::denounce_vote(Origin::signed(ALICE), court_id, ALICE, vote_item, salt), - Error::::SelfDenounceDisallowed + Error::::CallerDenouncedItself ); }); } @@ -1337,7 +1337,7 @@ fn denounce_vote_fails_if_invalid_reveal() { let invalid_vote_item = VoteItem::Outcome(invalid_outcome); assert_noop!( Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, invalid_vote_item, salt), - Error::::InvalidReveal + Error::::CommitmentHashMismatch ); }); } @@ -1360,7 +1360,7 @@ fn denounce_vote_fails_if_juror_not_voted() { assert_noop!( Court::denounce_vote(Origin::signed(BOB), court_id, ALICE, vote_item, salt), - Error::::JurorNotVoted + Error::::JurorDidNotVote ); }); } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 27f7e45f1..51736d191 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3234,7 +3234,7 @@ fn it_resolves_a_disputed_court_market() { let wrong_salt = BlakeTwo256::hash_of(&69); assert_noop!( Court::reveal_vote(Origin::signed(juror_2), market_id, vote_item_0.clone(), wrong_salt), - CError::::InvalidReveal + CError::::CommitmentHashMismatch ); assert_ok!(Court::reveal_vote( Origin::signed(juror_2), @@ -3250,7 +3250,7 @@ fn it_resolves_a_disputed_court_market() { assert_noop!( Court::reveal_vote(Origin::signed(juror_4), market_id, vote_item_0.clone(), salt), - CError::::JurorNotVoted + CError::::JurorDidNotVote ); // juror_5 fails to reveal in time From 63b72d8541a3770f2cf65eb1251908ad0a219fee Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 19 May 2023 15:31:26 +0200 Subject: [PATCH 326/368] fix get_valid_delegated_juror --- zrml/court/src/lib.rs | 53 +++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 77fd4dca1..65d767e7e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -52,7 +52,7 @@ use frame_system::{ ensure_signed, pallet_prelude::{BlockNumberFor, OriginFor}, }; -use rand::{Rng, RngCore, SeedableRng}; +use rand::{seq::SliceRandom, Rng, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; use sp_arithmetic::{per_things::Perquintill, traits::One}; use sp_runtime::{ @@ -1408,42 +1408,28 @@ mod pallet { } } - /// Return the first valid active juror starting - /// from the `random_number` index out of the `delegations`. - fn get_valid_delegated_juror( - delegations: &[T::AccountId], - random_number: u128, - ) -> Option { + /// Return one delegated juror out of the delegations randomly. + fn get_valid_delegated_juror(delegations: &[T::AccountId]) -> Option { + let mut rng = Self::rng(); let pool: CourtPoolOf = CourtPool::::get(); - let mut delegated_juror = None; + let mut valid_delegated_jurors = Vec::new(); - for count in 0..delegations.len() { - let delegation_index = (random_number.saturating_add(count as u128) - % delegations.len() as u128) as usize; - delegated_juror = match delegations.get(delegation_index) { - Some(del_j) => Some(del_j.clone()), - None => { - log::error!("Delegation with modulo index should exist!"); - debug_assert!(false); - None + for delegated_juror in delegations { + if let Some(delegated_juror_info) = >::get(delegated_juror) { + if delegated_juror_info.delegations.is_some() { + // skip if delegated juror is delegator herself + continue; } - }; - - if let Some(del_j) = &delegated_juror { - if let Some(delegated_juror_info) = >::get(del_j) { - if delegated_juror_info.delegations.is_some() { - // skip if delegated juror is delegator herself - continue; - } - if Self::get_pool_item(&pool, delegated_juror_info.stake, del_j).is_some() { - delegated_juror = Some(del_j.clone()); - break; - } + if Self::get_pool_item(&pool, delegated_juror_info.stake, delegated_juror) + .is_some() + { + valid_delegated_jurors.push(delegated_juror.clone()); + break; } } } - delegated_juror + valid_delegated_jurors.choose(&mut rng).cloned() } /// Add a juror or delegator with the provided `lock_added` to the `selections` map. @@ -1451,15 +1437,13 @@ mod pallet { selections: &mut BTreeMap>, court_participant: &T::AccountId, lock_added: BalanceOf, - random_number: u128, ) -> Result<(), SelectionError> { let delegations_opt = >::get(court_participant.clone()) .and_then(|p_info| p_info.delegations); match delegations_opt { Some(delegations) => { - let delegated_juror = - Self::get_valid_delegated_juror(delegations.as_slice(), random_number) - .ok_or(SelectionError::NoValidDelegatedJuror)?; + let delegated_juror = Self::get_valid_delegated_juror(delegations.as_slice()) + .ok_or(SelectionError::NoValidDelegatedJuror)?; // delegated juror gets the vote weight let sel_add = SelectionAdd::DelegationWeight; @@ -1520,7 +1504,6 @@ mod pallet { &mut selections, &pool_item.court_participant, lock_added, - random_section_start, ) { Ok(()) => {} Err(SelectionError::NoValidDelegatedJuror) => { From b5445a01bc6a8d011cbcb82bec8ebe8b7c096598 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 19 May 2023 15:32:54 +0200 Subject: [PATCH 327/368] remove wrong break --- zrml/court/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 65d767e7e..02f6c9e40 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1424,7 +1424,6 @@ mod pallet { .is_some() { valid_delegated_jurors.push(delegated_juror.clone()); - break; } } } From 5ab27e9abfd2e854c552e1e6f69432e96fe254c1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 19 May 2023 15:54:21 +0200 Subject: [PATCH 328/368] update weight for select jurors --- zrml/court/src/weights.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 3f7f915c1..5702c2fed 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -199,15 +199,15 @@ impl WeightInfoZeitgeist for WeightInfo { .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(j.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(j.into()))) } - // Storage: Court JurorPool (r:1 w:1) - // Storage: Court JurorsSelectionNonce (r:1 w:1) + // Storage: Court CourtPool (r:1 w:1) + // Storage: Court SelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - // Storage: Court Jurors (r:32 w:31) - fn select_jurors(a: u32) -> Weight { - Weight::from_ref_time(513_126_000) - // Standard Error: 7_829_000 - .saturating_add(Weight::from_ref_time(1_640_272_000).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(21)) + // Storage: Court Participants (r:35 w:31) + fn select_jurors(a: u32, ) -> Weight { + Weight::from_ref_time(639_560_000) + // Standard Error: 11_776_000 + .saturating_add(Weight::from_ref_time(2_310_239_000).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(24)) .saturating_add(T::DbWeight::get().reads((60_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(19)) .saturating_add(T::DbWeight::get().writes((60_u64).saturating_mul(a.into()))) From 38e8b1ef0e7594cdaa6ca8264af22aba087c27dd Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 22 May 2023 11:34:22 +0200 Subject: [PATCH 329/368] allow rejoin after exit preparation --- zrml/court/src/lib.rs | 85 ++++++++++++++++++++++----------------- zrml/court/src/tests.rs | 14 ++++--- zrml/court/src/weights.rs | 2 +- 3 files changed, 57 insertions(+), 44 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 02f6c9e40..2dcddd3a1 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -390,9 +390,6 @@ mod pallet { /// In order to exit the court the juror has to exit /// the pool first with `prepare_exit_court`. AlreadyPreparedExit, - /// The juror or delegator is not part of the court pool. - /// The court participant needs to exit the court and then rejoin. - ParticipantNotInCourtPool, /// The juror was not randomly selected for the court. JurorNotDrawn, /// The juror was drawn but did not manage to commitmently vote within the court. @@ -1123,44 +1120,56 @@ mod pallet { let now = >::block_number(); - let (active_lock, consumed_stake, joined_at) = if let Some(prev_p_info) = - >::get(who) - { - ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); - let (index, pool_item) = Self::get_pool_item(&pool, prev_p_info.stake, who) - .ok_or(Error::::ParticipantNotInCourtPool)?; - debug_assert!( - prev_p_info.prepare_exit_at.is_none(), - "If the pool item is found, the prepare_exit_at could have never been written." - ); - let consumed_stake = pool_item.consumed_stake; - let joined_at = pool_item.joined_at; - pool.remove(index); - (prev_p_info.active_lock, consumed_stake, joined_at) - } else { - if pool.is_full() { - let lowest_item = pool.first(); - let lowest_stake = lowest_item - .map(|pool_item| pool_item.stake) - .unwrap_or_else(>::zero); - debug_assert!({ - let mut sorted = pool.clone(); - sorted.sort_by_key(|pool_item| { - (pool_item.stake, pool_item.court_participant.clone()) + let remove_weakest_if_full = + |mut p: CourtPoolOf| -> Result, DispatchError> { + if p.is_full() { + let lowest_item = p.first(); + let lowest_stake = lowest_item + .map(|pool_item| pool_item.stake) + .unwrap_or_else(>::zero); + debug_assert!({ + let mut sorted = p.clone(); + sorted.sort_by_key(|pool_item| { + (pool_item.stake, pool_item.court_participant.clone()) + }); + p.len() == sorted.len() + && p.iter() + .zip(sorted.iter()) + .all(|(a, b)| lowest_stake <= a.stake && a == b) }); - pool.len() == sorted.len() - && pool - .iter() - .zip(sorted.iter()) - .all(|(a, b)| lowest_stake <= a.stake && a == b) - }); - ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); - // remove the lowest staked court participant - pool.remove(0); + ensure!(amount > lowest_stake, Error::::AmountBelowLowestJuror); + // remove the lowest staked court participant + p.remove(0); + } + + Ok(p) + }; + + let mut active_lock = >::zero(); + let mut consumed_stake = >::zero(); + let mut joined_at = now; + + if let Some(prev_p_info) = >::get(who) { + ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); + + if let Some((index, pool_item)) = Self::get_pool_item(&pool, prev_p_info.stake, who) + { + active_lock = prev_p_info.active_lock; + consumed_stake = pool_item.consumed_stake; + joined_at = pool_item.joined_at; + + pool.remove(index); + } else { + active_lock = prev_p_info.active_lock; + consumed_stake = prev_p_info.active_lock; + + pool = remove_weakest_if_full(pool)?; } + } else { + pool = remove_weakest_if_full(pool)?; + } - (>::zero(), >::zero(), now) - }; + let (active_lock, consumed_stake, joined_at) = (active_lock, consumed_stake, joined_at); match pool.binary_search_by_key(&(amount, who), |pool_item| { (pool_item.stake, &pool_item.court_participant) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index fcf000657..b853965d2 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -407,18 +407,22 @@ fn join_court_fails_amount_below_last_join() { } #[test] -fn join_court_fails_juror_needs_to_exit() { +fn join_court_after_prepare_exit_court() { ExtBuilder::default().build().execute_with(|| { let min = MinJurorStake::get(); let amount = 2 * min; + let now = >::block_number(); assert_ok!(Court::join_court(Origin::signed(ALICE), amount)); assert_ok!(Court::prepare_exit_court(Origin::signed(ALICE))); - assert_noop!( - Court::join_court(Origin::signed(ALICE), amount + 1), - Error::::ParticipantNotInCourtPool - ); + let p_info = >::get(ALICE).unwrap(); + assert_eq!(Some(now), p_info.prepare_exit_at); + + assert_ok!(Court::join_court(Origin::signed(ALICE), amount + 1)); + + let p_info = >::get(ALICE).unwrap(); + assert_eq!(None, p_info.prepare_exit_at); }); } diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 5702c2fed..9cf8d070a 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -203,7 +203,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) // Storage: Court Participants (r:35 w:31) - fn select_jurors(a: u32, ) -> Weight { + fn select_jurors(a: u32) -> Weight { Weight::from_ref_time(639_560_000) // Standard Error: 11_776_000 .saturating_add(Weight::from_ref_time(2_310_239_000).saturating_mul(a.into())) From b434ba5bce5951f25f2ee4dbaa000193941e1ccb Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 22 May 2023 15:22:38 +0200 Subject: [PATCH 330/368] remove unnecessary apis --- runtime/common/src/lib.rs | 50 ------------------------- zrml/court/src/lib.rs | 46 ++++++++++++----------- zrml/court/src/mock.rs | 58 ++--------------------------- zrml/court/src/traits.rs | 39 ------------------- zrml/prediction-markets/src/mock.rs | 57 +--------------------------- 5 files changed, 31 insertions(+), 219 deletions(-) delete mode 100644 zrml/court/src/traits.rs diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 031debb13..5e14ad187 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -931,56 +931,8 @@ macro_rules! impl_config_traits { type WeightInfo = zrml_authorized::weights::WeightInfo; } - use zrml_court::Error as CourtError; - use zrml_court::types::VoteItem; - use zrml_court::traits::{VoteCheckApi, AppealCheckApi, DefaultWinnerApi}; - use frame_support::ensure; - use zrml_market_commons::MarketCommonsPalletApi; - - pub struct AppealCheck; - impl AppealCheckApi for AppealCheck { - type MarketId = MarketId; - - fn pre_appeal(market_id: &Self::MarketId) -> Result<(), sp_runtime::DispatchError> { - let market = MarketCommons::market(market_id)?; - ensure!(market.status == MarketStatus::Disputed, CourtError::::MarketIsNotDisputed); - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Court, - CourtError::::MarketDoesNotHaveCourtMechanism - ); - Ok(()) - } - } - - pub struct VoteCheck; - impl VoteCheckApi for VoteCheck { - type MarketId = MarketId; - - fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), sp_runtime::DispatchError> { - let market = MarketCommons::market(&market_id)?; - ensure!( - market.matches_outcome_report(&vote_item.clone().into_outcome().unwrap()), - CourtError::::OutcomeMismatch - ); - Ok(()) - } - } - - pub struct DefaultWinner; - impl DefaultWinnerApi for DefaultWinner { - type MarketId = MarketId; - - fn default_winner(market_id: &Self::MarketId) -> Result { - let market = MarketCommons::market(market_id)?; - let report = market.report.as_ref().ok_or(CourtError::::MarketReportNotFound)?; - let vote_item = VoteItem::Outcome(report.outcome.clone()); - Ok(vote_item) - } - } - impl zrml_court::Config for Runtime { type AppealBond = AppealBond; - type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; type VotePeriod = CourtVotePeriod; type AggregationPeriod = CourtAggregationPeriod; @@ -988,7 +940,6 @@ macro_rules! impl_config_traits { type LockId = CourtLockId; type PalletId = CourtPalletId; type Currency = Balances; - type DefaultWinner = DefaultWinner; type DisputeResolution = zrml_prediction_markets::Pallet; type Event = Event; type InflationPeriod = InflationPeriod; @@ -1003,7 +954,6 @@ macro_rules! impl_config_traits { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; - type VoteCheck = VoteCheck; type WeightInfo = zrml_court::weights::WeightInfo; } diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 2dcddd3a1..67d8749de 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -23,11 +23,9 @@ extern crate alloc; use crate::{ - traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, - weights::WeightInfoZeitgeist, - AppealInfo, CourtId, CourtInfo, CourtParticipantInfo, CourtPoolItem, CourtStatus, Draw, - JurorVoteWithStakes, RawCommitment, RoundTiming, SelectionAdd, SelectionError, SelectionValue, - SelfInfo, Vote, VoteItem, VoteItemType, + weights::WeightInfoZeitgeist, AppealInfo, CourtId, CourtInfo, CourtParticipantInfo, + CourtPoolItem, CourtStatus, Draw, JurorVoteWithStakes, RawCommitment, RoundTiming, + SelectionAdd, SelectionError, SelectionValue, SelfInfo, Vote, VoteItem, VoteItemType, }; use alloc::{ collections::{BTreeMap, BTreeSet}, @@ -62,7 +60,7 @@ use sp_runtime::{ use zeitgeist_primitives::{ traits::{DisputeApi, DisputeMaxWeightApi, DisputeResolutionApi}, types::{ - Asset, GlobalDisputeItem, Market, MarketDisputeMechanism, OutcomeReport, + Asset, GlobalDisputeItem, Market, MarketDisputeMechanism, MarketStatus, OutcomeReport, ResultWithWeightInfo, }, }; @@ -74,7 +72,6 @@ pub mod migrations; mod mock; mod mock_storage; mod tests; -pub mod traits; pub mod types; pub mod weights; @@ -93,9 +90,6 @@ mod pallet { #[pallet::constant] type AppealBond: Get>; - /// The functionality to check an appeal beforehand. - type AppealCheck: AppealCheckApi>; - /// The expected blocks per year to calculate the inflation emission. #[pallet::constant] type BlocksPerYear: Get; @@ -125,9 +119,6 @@ mod pallet { + NamedReservableCurrency + LockableCurrency; - /// The functionality to get a default winner if no juror voted inside a court. - type DefaultWinner: DefaultWinnerApi>; - /// The functionality to allow controlling the markets resolution time. type DisputeResolution: DisputeResolutionApi< AccountId = Self::AccountId, @@ -194,9 +185,6 @@ mod pallet { #[pallet::constant] type TreasuryPalletId: Get; - /// The functionality to check a vote item beforehand. - type VoteCheck: VoteCheckApi>; - /// Weights generated by benchmarks type WeightInfo: WeightInfoZeitgeist; } @@ -451,6 +439,8 @@ mod pallet { WinnerVoteItemIsNoOutcome, /// The outcome does not match the market outcomes. OutcomeMismatch, + /// The vote item was expected to be an outcome, but is actually not an outcome. + VoteItemIsNoOutcome, } #[pallet::hooks] @@ -755,7 +745,10 @@ mod pallet { let denouncer = ensure_signed(origin)?; if let Some(market_id) = >::get(court_id) { - T::VoteCheck::pre_validate(&market_id, vote_item.clone())?; + let market = T::MarketCommons::market(&market_id)?; + let outcome = + vote_item.clone().into_outcome().ok_or(Error::::VoteItemIsNoOutcome)?; + ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); } let juror = T::Lookup::lookup(juror)?; @@ -830,7 +823,10 @@ mod pallet { let who = ensure_signed(origin)?; if let Some(market_id) = >::get(court_id) { - T::VoteCheck::pre_validate(&market_id, vote_item.clone())?; + let market = T::MarketCommons::market(&market_id)?; + let outcome = + vote_item.clone().into_outcome().ok_or(Error::::VoteItemIsNoOutcome)?; + ensure!(market.matches_outcome_report(&outcome), Error::::OutcomeMismatch); } ensure!( @@ -1710,7 +1706,12 @@ mod pallet { now: T::BlockNumber, ) -> Result<(), DispatchError> { if let Some(market_id) = >::get(court_id) { - T::AppealCheck::pre_appeal(&market_id)?; + let market = T::MarketCommons::market(&market_id)?; + ensure!(market.status == MarketStatus::Disputed, Error::::MarketIsNotDisputed); + ensure!( + market.dispute_mechanism == MarketDisputeMechanism::Court, + Error::::MarketDoesNotHaveCourtMechanism + ); } ensure!( @@ -1949,8 +1950,11 @@ mod pallet { .unwrap_or(None); let market_id = >::get(court_id) .ok_or(Error::::CourtIdToMarketIdNotFound)?; - let default: VoteItem = T::DefaultWinner::default_winner(&market_id)?; - let winner_vote_item = Self::get_winner(last_draws, last_winner).unwrap_or(default); + let market = T::MarketCommons::market(&market_id)?; + let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; + let default_vote_item = VoteItem::Outcome(report.outcome.clone()); + let winner_vote_item = + Self::get_winner(last_draws, last_winner).unwrap_or(default_vote_item); Ok(winner_vote_item) } diff --git a/zrml/court/src/mock.rs b/zrml/court/src/mock.rs index 27de62a12..085f82d5e 100644 --- a/zrml/court/src/mock.rs +++ b/zrml/court/src/mock.rs @@ -18,14 +18,9 @@ #![cfg(test)] -use crate::{ - self as zrml_court, - mock_storage::pallet as mock_storage, - traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, - Error as PError, VoteItem, -}; +use crate::{self as zrml_court, mock_storage::pallet as mock_storage}; use frame_support::{ - construct_runtime, ensure, ord_parameter_types, + construct_runtime, ord_parameter_types, pallet_prelude::{DispatchError, Weight}, parameter_types, traits::{Everything, Hooks, NeverEnsureOrigin}, @@ -45,11 +40,10 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, - MarketDisputeMechanism, MarketId, MarketStatus, Moment, UncheckedExtrinsicTest, + AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, + Moment, UncheckedExtrinsicTest, }, }; -use zrml_market_commons::MarketCommonsPalletApi; pub const ALICE: AccountIdTest = 0; pub const BOB: AccountIdTest = 1; @@ -139,57 +133,14 @@ impl DisputeResolutionApi for MockResolution { } } -pub struct AppealCheck; -impl AppealCheckApi for AppealCheck { - type MarketId = MarketId; - - fn pre_appeal(market_id: &Self::MarketId) -> Result<(), DispatchError> { - let market = MarketCommons::market(market_id)?; - ensure!(market.status == MarketStatus::Disputed, PError::::MarketIsNotDisputed); - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Court, - PError::::MarketDoesNotHaveCourtMechanism - ); - Ok(()) - } -} - -pub struct VoteCheck; -impl VoteCheckApi for VoteCheck { - type MarketId = MarketId; - - fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), DispatchError> { - let market = MarketCommons::market(market_id)?; - ensure!( - market.matches_outcome_report(&vote_item.into_outcome().unwrap()), - PError::::OutcomeMismatch - ); - Ok(()) - } -} - -pub struct DefaultWinner; -impl DefaultWinnerApi for DefaultWinner { - type MarketId = MarketId; - - fn default_winner(market_id: &Self::MarketId) -> Result { - let market = MarketCommons::market(market_id)?; - let report = market.report.as_ref().ok_or(PError::::MarketReportNotFound)?; - let vote_item = VoteItem::Outcome(report.outcome.clone()); - Ok(vote_item) - } -} - impl crate::Config for Runtime { type AppealBond = AppealBond; - type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; type LockId = LockId; type Currency = Balances; type VotePeriod = VotePeriod; type AggregationPeriod = AggregationPeriod; type AppealPeriod = AppealPeriod; - type DefaultWinner = DefaultWinner; type DisputeResolution = MockResolution; type Event = Event; type InflationPeriod = InflationPeriod; @@ -205,7 +156,6 @@ impl crate::Config for Runtime { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; - type VoteCheck = VoteCheck; type WeightInfo = crate::weights::WeightInfo; } diff --git a/zrml/court/src/traits.rs b/zrml/court/src/traits.rs deleted file mode 100644 index cd5f76e7c..000000000 --- a/zrml/court/src/traits.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2022-2023 Forecasting Technologies LTD. -// Copyright 2021-2022 Zeitgeist PM LLC. -// -// This file is part of Zeitgeist. -// -// Zeitgeist is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the -// Free Software Foundation, either version 3 of the License, or (at -// your option) any later version. -// -// Zeitgeist is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Zeitgeist. If not, see . - -use frame_support::dispatch::DispatchError; - -use crate::types::VoteItem; - -pub trait VoteCheckApi { - type MarketId; - - fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), DispatchError>; -} - -pub trait AppealCheckApi { - type MarketId; - - fn pre_appeal(market_id: &Self::MarketId) -> Result<(), DispatchError>; -} - -pub trait DefaultWinnerApi { - type MarketId; - - fn default_winner(market_id: &Self::MarketId) -> Result; -} diff --git a/zrml/prediction-markets/src/mock.rs b/zrml/prediction-markets/src/mock.rs index 4cc608e83..d4d4d179c 100644 --- a/zrml/prediction-markets/src/mock.rs +++ b/zrml/prediction-markets/src/mock.rs @@ -24,9 +24,7 @@ use crate as prediction_markets; use frame_support::{ - construct_runtime, ensure, ord_parameter_types, - pallet_prelude::DispatchError, - parameter_types, + construct_runtime, ord_parameter_types, parameter_types, traits::{Everything, NeverEnsureOrigin, OnFinalize, OnInitialize}, }; use frame_system::{EnsureRoot, EnsureSignedBy}; @@ -54,16 +52,9 @@ use zeitgeist_primitives::{ }, types::{ AccountIdTest, Amount, Asset, Balance, BasicCurrencyAdapter, BlockNumber, BlockTest, - CurrencyId, Hash, Index, MarketDisputeMechanism, MarketId, MarketStatus, Moment, PoolId, - SerdeWrapper, UncheckedExtrinsicTest, + CurrencyId, Hash, Index, MarketId, Moment, PoolId, SerdeWrapper, UncheckedExtrinsicTest, }, }; -use zrml_court::{ - traits::{AppealCheckApi, DefaultWinnerApi, VoteCheckApi}, - types::VoteItem, - Error as CError, -}; -use zrml_market_commons::MarketCommonsPalletApi; use zeitgeist_primitives::constants::mock::{ GlobalDisputeLockId, GlobalDisputePeriod, GlobalDisputesPalletId, MaxGlobalDisputeVotes, @@ -256,50 +247,8 @@ impl zrml_authorized::Config for Runtime { type WeightInfo = zrml_authorized::weights::WeightInfo; } -pub struct AppealCheck; -impl AppealCheckApi for AppealCheck { - type MarketId = MarketId; - - fn pre_appeal(market_id: &Self::MarketId) -> Result<(), DispatchError> { - let market = MarketCommons::market(market_id)?; - ensure!(market.status == MarketStatus::Disputed, CError::::MarketIsNotDisputed); - ensure!( - market.dispute_mechanism == MarketDisputeMechanism::Court, - CError::::MarketDoesNotHaveCourtMechanism - ); - Ok(()) - } -} - -pub struct VoteCheck; -impl VoteCheckApi for VoteCheck { - type MarketId = MarketId; - - fn pre_validate(market_id: &Self::MarketId, vote_item: VoteItem) -> Result<(), DispatchError> { - let market = MarketCommons::market(market_id)?; - ensure!( - market.matches_outcome_report(&vote_item.into_outcome().unwrap()), - CError::::OutcomeMismatch - ); - Ok(()) - } -} - -pub struct DefaultWinner; -impl DefaultWinnerApi for DefaultWinner { - type MarketId = MarketId; - - fn default_winner(market_id: &Self::MarketId) -> Result { - let market = MarketCommons::market(market_id)?; - let report = market.report.as_ref().ok_or(CError::::MarketReportNotFound)?; - let vote_item = VoteItem::Outcome(report.outcome.clone()); - Ok(vote_item) - } -} - impl zrml_court::Config for Runtime { type AppealBond = AppealBond; - type AppealCheck = AppealCheck; type BlocksPerYear = BlocksPerYear; type DisputeResolution = prediction_markets::Pallet; type VotePeriod = VotePeriod; @@ -307,7 +256,6 @@ impl zrml_court::Config for Runtime { type AppealPeriod = AppealPeriod; type LockId = LockId; type Currency = Balances; - type DefaultWinner = DefaultWinner; type Event = Event; type InflationPeriod = InflationPeriod; type MarketCommons = MarketCommons; @@ -322,7 +270,6 @@ impl zrml_court::Config for Runtime { type RequestInterval = RequestInterval; type Slash = Treasury; type TreasuryPalletId = TreasuryPalletId; - type VoteCheck = VoteCheck; type WeightInfo = zrml_court::weights::WeightInfo; } From e05f9fd67f1d59f0c8cf249e8343ee5073c7c499 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 24 May 2023 11:53:02 +0200 Subject: [PATCH 331/368] improve naming and docs --- zrml/court/src/benchmarks.rs | 4 +- zrml/court/src/lib.rs | 88 +++++++++++++++++++----------------- zrml/court/src/tests.rs | 8 ++-- zrml/court/src/types.rs | 9 ++-- zrml/court/src/weights.rs | 4 +- 5 files changed, 61 insertions(+), 52 deletions(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 9e2711322..1cec04d1e 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -570,13 +570,13 @@ benchmarks! { Court::::handle_inflation(now); } - select_jurors { + select_participants { let a in 0..(T::MaxAppeals::get() - 1); fill_pool::(T::MaxCourtParticipants::get())?; fill_delegations::(); }: { - let _ = Court::::select_jurors(a as usize).unwrap(); + let _ = Court::::select_participants(a as usize).unwrap(); } on_dispute { diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 67d8749de..086327f02 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -148,11 +148,14 @@ mod pallet { /// The maximum number of randomly selected n * `MinJurorStake` (n equals all draw weights) /// out of all jurors and delegators stake. This configuration parameter should be /// the maximum necessary_draws_weight multiplied by 2. + /// Each `MinJurorStake` (draw weight) out of `n * MinJurorStake` belongs + /// to one juror or one delegator. /// (necessary_draws_weight = 2^(appeals_len) * 31 + 2^(appeals_len) - 1) /// Assume MaxAppeals - 1 (= 3), example: 2^3 * 31 + 2^3 - 1 = 255 /// => 2 * 255 = 510 = `MaxSelectedDraws`. - /// Why the multiplication by two? Because with delegations each juror draw weight - /// could potentially delegate an additional juror in addition to the delegator itself. + /// Why the multiplication by two? + /// Because each draw weight is associated with one juror account id and + /// potentially a delegator account id. #[pallet::constant] type MaxSelectedDraws: Get; @@ -189,14 +192,14 @@ mod pallet { type WeightInfo: WeightInfoZeitgeist; } - // Number of draws for an initial market dispute. + // Number of draws for the initial court round. const INITIAL_DRAWS_NUM: usize = 31; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); // Weight used to increase the number of jurors for subsequent appeals - // of the same market + // of the same court. const APPEAL_BASIS: usize = 2; - // Basis used to increase the bond for subsequent appeals of the same market + // Basis used to increase the bond for subsequent appeals of the same market. const APPEAL_BOND_BASIS: u32 = 2; pub(crate) type AccountIdOf = ::AccountId; @@ -241,7 +244,7 @@ mod pallet { pub struct Pallet(PhantomData); /// The pool of jurors and delegators who can get randomly selected according to their stake. - /// The pool is sorted by stake in ascending order [min, ..., max]. + /// The pool is sorted by `stake` in ascending order [min, ..., max]. #[pallet::storage] pub type CourtPool = StorageValue<_, CourtPoolOf, ValueQuery>; @@ -667,7 +670,7 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n)`, where `n` is the number of jurors + /// Complexity: `O(log(n))`, where `n` is the number of participants /// in the list of random selections (draws). #[pallet::weight(T::WeightInfo::vote(T::MaxSelectedDraws::get()))] #[transactional] @@ -718,7 +721,7 @@ mod pallet { /// Denounce a juror during the voting period for which the commitment vote is known. /// This is useful to punish the behaviour that jurors reveal - /// their commitments before the voting period ends. + /// their commitments to others before the voting period ends. /// A check of `commitment_hash == hash(juror ++ vote_item ++ salt)` /// is performed for validation. /// @@ -732,7 +735,8 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n)`, where `n` is the number of selected draws. + /// Complexity: `O(log(n))`, where `n` is the number of selected draws + /// in the specified court. #[pallet::weight(T::WeightInfo::denounce_vote(T::MaxSelectedDraws::get()))] #[transactional] pub fn denounce_vote( @@ -800,8 +804,9 @@ mod pallet { Ok(Some(T::WeightInfo::denounce_vote(draws_len)).into()) } - /// Reveal the commitment vote of the caller juror. - /// A check of `commitment_hash == hash(juror ++ vote_item ++ salt)` is performed for validation. + /// Reveal the commitment vote of the caller, who is a selected juror. + /// A check of `commitment_hash == hash(juror ++ vote_item ++ salt)` + /// is performed for validation. /// /// # Arguments /// @@ -811,7 +816,8 @@ mod pallet { /// /// # Weight /// - /// Complexity: `O(n)`, where `n` is the number of selected draws. + /// Complexity: `O(log(n))`, where `n` is the number of selected draws + /// in the specified court. #[pallet::weight(T::WeightInfo::reveal_vote(T::MaxSelectedDraws::get()))] #[transactional] pub fn reveal_vote( @@ -881,7 +887,7 @@ mod pallet { /// /// # Weight /// - /// Complexity: It depends heavily on the complexity of `select_jurors`. + /// Complexity: It depends heavily on the complexity of `select_participants`. #[pallet::weight(T::WeightInfo::appeal( T::MaxCourtParticipants::get(), T::MaxAppeals::get(), @@ -915,13 +921,13 @@ mod pallet { let last_resolve_at = court.round_ends.appeal; - // used for benchmarking, juror pool is queried inside `select_jurors` + // used for benchmarking, juror pool is queried inside `select_participants` let pool_len = >::decode_len().unwrap_or(0) as u32; let mut ids_len_1 = 0u32; // if appeal_number == MaxAppeals, then don't start a new appeal round if appeal_number < T::MaxAppeals::get() as usize { - let new_draws = Self::select_jurors(appeal_number)?; + let new_draws = Self::select_participants(appeal_number)?; let request_block = >::get(); debug_assert!(request_block >= now, "Request block must be greater than now."); let round_timing = RoundTiming { @@ -1266,12 +1272,12 @@ mod pallet { Weight::zero() } - // Get `n` unique and ordered random `MinJurorStake` section starts + // Get `n` unique and ordered random `MinJurorStake` section ends // from the random number generator. // Uses Partial Fisher Yates shuffle and drawing without replacement. // The time complexity is O(n). - // Return a vector of n unique random numbers between 1 and max (inclusive). - pub(crate) fn get_n_random_section_starts( + // Return a vector of n unique random numbers between ´MinJurorStake´ and ´max´ (inclusive). + pub(crate) fn get_n_random_section_ends( n: usize, max: u128, ) -> Result, DispatchError> { @@ -1283,7 +1289,7 @@ mod pallet { debug_assert!(sections_len >= (n as u128)); let mut swaps = BTreeMap::::new(); - let mut random_section_starts = BTreeSet::new(); + let mut random_section_ends = BTreeSet::new(); for i in 0..(n as u128) { let visited_i = *swaps.get(&i).unwrap_or(&i); @@ -1300,13 +1306,13 @@ mod pallet { // add one because we need numbers between 1 and sections_len (inclusive) let random_index = unused_random_number.saturating_add(1); - let random_section_start = random_index.saturating_mul(min_juror_stake); - random_section_starts.insert(random_section_start); + let random_section_end = random_index.saturating_mul(min_juror_stake); + random_section_ends.insert(random_section_end); } - debug_assert!(random_section_starts.len() == n); + debug_assert!(random_section_ends.len() == n); - Ok(random_section_starts) + Ok(random_section_ends) } // Adds active lock amount. @@ -1475,30 +1481,30 @@ mod pallet { // If a delegator is chosen by a random number, one delegated juror gets the vote weight. fn get_selections( pool: &mut CourtPoolOf, - random_section_starts: BTreeSet, - cumulative_section_starts: Vec<(u128, bool)>, + random_section_ends: BTreeSet, + cumulative_section_ends: Vec<(u128, bool)>, ) -> BTreeMap> { - debug_assert!(pool.len() == cumulative_section_starts.len()); + debug_assert!(pool.len() == cumulative_section_ends.len()); debug_assert!({ - let prev = cumulative_section_starts.clone(); - let mut sorted = cumulative_section_starts.clone(); + let prev = cumulative_section_ends.clone(); + let mut sorted = cumulative_section_ends.clone(); sorted.sort(); prev.len() == sorted.len() && prev.iter().zip(sorted.iter()).all(|(a, b)| a == b) }); debug_assert!({ - random_section_starts.iter().all(|random_section_start| { - let last = cumulative_section_starts.last().unwrap_or(&(0, false)).0; - *random_section_start <= last + random_section_ends.iter().all(|random_section_end| { + let last = cumulative_section_ends.last().unwrap_or(&(0, false)).0; + *random_section_end <= last }) }); let mut selections = BTreeMap::>::new(); let mut invalid_juror_indices = Vec::::new(); - for random_section_start in random_section_starts { + for random_section_end in random_section_ends { let allow_zero_stake = false; - let range_index = cumulative_section_starts - .binary_search(&(random_section_start, allow_zero_stake)) + let range_index = cumulative_section_ends + .binary_search(&(random_section_end, allow_zero_stake)) .unwrap_or_else(|i| i); if let Some(pool_item) = pool.get_mut(range_index) { let unconsumed = pool_item.stake.saturating_sub(pool_item.consumed_stake); @@ -1582,7 +1588,7 @@ mod pallet { let min_juror_stake = T::MinJurorStake::get().saturated_into::(); let mut total_unconsumed = 0u128; - let mut cumulative_section_starts = Vec::new(); + let mut cumulative_section_ends = Vec::new(); let mut running_total = 0u128; for pool_item in &pool { let unconsumed = pool_item @@ -1598,7 +1604,7 @@ mod pallet { // might take the wrong juror (with zero stake) // (running total would be the same for two consecutive jurors) let zero_stake = unconsumed.is_zero(); - cumulative_section_starts.push((running_total, zero_stake)); + cumulative_section_ends.push((running_total, zero_stake)); } debug_assert!( (total_unconsumed % min_juror_stake).is_zero(), @@ -1610,10 +1616,10 @@ mod pallet { total_unconsumed >= required_stake, Error::::NotEnoughJurorsAndDelegatorsStake ); - let random_section_starts = - Self::get_n_random_section_starts(draw_weight, total_unconsumed)?; + let random_section_ends = + Self::get_n_random_section_ends(draw_weight, total_unconsumed)?; let selections = - Self::get_selections(&mut pool, random_section_starts, cumulative_section_starts); + Self::get_selections(&mut pool, random_section_ends, cumulative_section_ends); >::put(pool); Ok(Self::convert_selections_to_draws(selections)) @@ -1646,7 +1652,7 @@ mod pallet { // in further court rounds shrinks. // // Returns the new draws. - pub(crate) fn select_jurors( + pub(crate) fn select_participants( appeal_number: usize, ) -> Result, DispatchError> { let necessary_draws_weight = Self::necessary_draws_weight(appeal_number); @@ -2057,7 +2063,7 @@ mod pallet { let appeal_number = 0usize; let pool_len = >::decode_len().unwrap_or(0) as u32; - let new_draws = Self::select_jurors(appeal_number)?; + let new_draws = Self::select_participants(appeal_number)?; let now = >::block_number(); let request_block = >::get(); diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index b853965d2..22e1b1da4 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -2615,7 +2615,7 @@ fn choose_multiple_weighted_works() { } #[test] -fn select_jurors_updates_juror_consumed_stake() { +fn select_participants_updates_juror_consumed_stake() { ExtBuilder::default().build().execute_with(|| { let court_id = initialize_court(); fill_juror_pool(MaxCourtParticipants::get()); @@ -2626,7 +2626,7 @@ fn select_jurors_updates_juror_consumed_stake() { let jurors = CourtPool::::get(); let consumed_stake_before = jurors.iter().map(|juror| juror.consumed_stake).sum::(); - let new_draws = Court::select_jurors(appeal_number).unwrap(); + let new_draws = Court::select_participants(appeal_number).unwrap(); let total_draw_slashable = new_draws.iter().map(|draw| draw.slashable).sum::(); let jurors = CourtPool::::get(); @@ -2640,7 +2640,7 @@ fn select_jurors_updates_juror_consumed_stake() { #[test_case(1usize; "second")] #[test_case(2usize; "third")] #[test_case(3usize; "fourth")] -fn select_jurors_fails_if_not_enough_jurors(appeal_number: usize) { +fn select_participants_fails_if_not_enough_jurors(appeal_number: usize) { ExtBuilder::default().build().execute_with(|| { let necessary_draws_weight = Court::necessary_draws_weight(appeal_number); for i in 0..(necessary_draws_weight - 1usize) { @@ -2651,7 +2651,7 @@ fn select_jurors_fails_if_not_enough_jurors(appeal_number: usize) { } assert_noop!( - Court::select_jurors(appeal_number), + Court::select_participants(appeal_number), Error::::NotEnoughJurorsAndDelegatorsStake ); }); diff --git a/zrml/court/src/types.rs b/zrml/court/src/types.rs index 017572274..e9c8535ff 100644 --- a/zrml/court/src/types.rs +++ b/zrml/court/src/types.rs @@ -179,13 +179,16 @@ pub struct RoundTiming { /// The end block of the pre-vote period. pub pre_vote: BlockNumber, /// The end block of the vote period. - /// Note this can also be used as the block duration for votes. + /// Note this can also be used as the block duration for votes, + /// if it is used for the initialisation of the court round ends. pub vote: BlockNumber, /// The end block of the aggregation period. - /// Note this can also be used as the block duration for revealing votes. + /// Note this can also be used as the block duration for revealing votes, + /// if it is used for the initialisation of the court round ends. pub aggregation: BlockNumber, /// The end block of the appeal period. - /// Note this can also be used as the block duration for appeals. + /// Note this can also be used as the block duration for appeals, + /// if it is used for the initialisation of the court round ends. pub appeal: BlockNumber, } diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 9cf8d070a..3f8357835 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -57,7 +57,7 @@ pub trait WeightInfoZeitgeist { fn reassign_court_stakes(d: u32) -> Weight; fn set_inflation() -> Weight; fn handle_inflation(j: u32) -> Weight; - fn select_jurors(a: u32) -> Weight; + fn select_participants(a: u32) -> Weight; fn on_dispute(j: u32, r: u32) -> Weight; fn on_resolution(d: u32) -> Weight; fn exchange(a: u32) -> Weight; @@ -203,7 +203,7 @@ impl WeightInfoZeitgeist for WeightInfo { // Storage: Court SelectionNonce (r:1 w:1) // Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) // Storage: Court Participants (r:35 w:31) - fn select_jurors(a: u32) -> Weight { + fn select_participants(a: u32) -> Weight { Weight::from_ref_time(639_560_000) // Standard Error: 11_776_000 .saturating_add(Weight::from_ref_time(2_310_239_000).saturating_mul(a.into())) From ead0bc506091442eeb619a855a9d69e690ecfabf Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 5 Jun 2023 10:04:09 +0200 Subject: [PATCH 332/368] fix clippy --- zrml/court/src/tests.rs | 45 +++++++++++++++++++++++----- zrml/prediction-markets/src/tests.rs | 34 +++++++++++++++++---- zrml/simple-disputes/Cargo.toml | 2 +- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index 9f99d8a43..916eb2ef7 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -21,8 +21,8 @@ extern crate alloc; use crate::{ mock::{ - run_blocks, run_to_block, Balances, Court, ExtBuilder, MarketCommons, RuntimeOrigin, Runtime, - System, ALICE, BOB, CHARLIE, DAVE, EVE, INITIAL_BALANCE, POOR_PAUL, + run_blocks, run_to_block, Balances, Court, ExtBuilder, MarketCommons, Runtime, + RuntimeOrigin, System, ALICE, BOB, CHARLIE, DAVE, EVE, INITIAL_BALANCE, POOR_PAUL, }, mock_storage::pallet::MarketIdsPerDisputeBlock, types::{CourtStatus, Draw, Vote, VoteItem}, @@ -987,7 +987,12 @@ fn reveal_vote_works() { run_blocks(VotePeriod::get() + 1); - assert_ok!(Court::reveal_vote(RuntimeOrigin::signed(ALICE), court_id, vote_item.clone(), salt,)); + assert_ok!(Court::reveal_vote( + RuntimeOrigin::signed(ALICE), + court_id, + vote_item.clone(), + salt, + )); System::assert_last_event( Event::JurorRevealedVote { juror: ALICE, court_id, vote_item: vote_item.clone(), salt } .into(), @@ -1157,7 +1162,12 @@ fn reveal_vote_fails_if_already_revealed() { let vote_item = VoteItem::Outcome(outcome); - assert_ok!(Court::reveal_vote(RuntimeOrigin::signed(ALICE), court_id, vote_item.clone(), salt)); + assert_ok!(Court::reveal_vote( + RuntimeOrigin::signed(ALICE), + court_id, + vote_item.clone(), + salt + )); assert_noop!( Court::reveal_vote(RuntimeOrigin::signed(ALICE), court_id, vote_item, salt), @@ -1340,7 +1350,13 @@ fn denounce_vote_fails_if_invalid_reveal() { let invalid_outcome = OutcomeReport::Scalar(69u128); let invalid_vote_item = VoteItem::Outcome(invalid_outcome); assert_noop!( - Court::denounce_vote(RuntimeOrigin::signed(BOB), court_id, ALICE, invalid_vote_item, salt), + Court::denounce_vote( + RuntimeOrigin::signed(BOB), + court_id, + ALICE, + invalid_vote_item, + salt + ), Error::::CommitmentHashMismatch ); }); @@ -1379,7 +1395,12 @@ fn denounce_vote_fails_if_vote_already_revealed() { let vote_item = VoteItem::Outcome(outcome); - assert_ok!(Court::reveal_vote(RuntimeOrigin::signed(ALICE), court_id, vote_item.clone(), salt)); + assert_ok!(Court::reveal_vote( + RuntimeOrigin::signed(ALICE), + court_id, + vote_item.clone(), + salt + )); assert_noop!( Court::reveal_vote(RuntimeOrigin::signed(ALICE), court_id, vote_item, salt), @@ -1571,7 +1592,12 @@ fn appeal_get_latest_resolved_outcome_changes() { let vote_item = VoteItem::Outcome(outcome); - assert_ok!(Court::reveal_vote(RuntimeOrigin::signed(ALICE), court_id, vote_item.clone(), salt)); + assert_ok!(Court::reveal_vote( + RuntimeOrigin::signed(ALICE), + court_id, + vote_item.clone(), + salt + )); run_blocks(AggregationPeriod::get() + 1); @@ -1595,7 +1621,10 @@ fn appeal_get_latest_resolved_outcome_changes() { #[test] fn appeal_fails_if_court_not_found() { ExtBuilder::default().build().execute_with(|| { - assert_noop!(Court::appeal(RuntimeOrigin::signed(CHARLIE), 0), Error::::CourtNotFound); + assert_noop!( + Court::appeal(RuntimeOrigin::signed(CHARLIE), 0), + Error::::CourtNotFound + ); }); } diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 2fed85632..02499f9a8 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -3250,11 +3250,21 @@ fn it_resolves_a_disputed_court_market() { vote_item_0.clone(), salt )); - assert_ok!(Court::reveal_vote(RuntimeOrigin::signed(juror_1), market_id, vote_item_1, salt)); + assert_ok!(Court::reveal_vote( + RuntimeOrigin::signed(juror_1), + market_id, + vote_item_1, + salt + )); let wrong_salt = BlakeTwo256::hash_of(&69); assert_noop!( - Court::reveal_vote(RuntimeOrigin::signed(juror_2), market_id, vote_item_0.clone(), wrong_salt), + Court::reveal_vote( + RuntimeOrigin::signed(juror_2), + market_id, + vote_item_0.clone(), + wrong_salt + ), CError::::CommitmentHashMismatch ); assert_ok!(Court::reveal_vote( @@ -3265,12 +3275,22 @@ fn it_resolves_a_disputed_court_market() { )); assert_noop!( - Court::reveal_vote(RuntimeOrigin::signed(juror_3), market_id, vote_item_0.clone(), salt), + Court::reveal_vote( + RuntimeOrigin::signed(juror_3), + market_id, + vote_item_0.clone(), + salt + ), CError::::VoteAlreadyDenounced ); assert_noop!( - Court::reveal_vote(RuntimeOrigin::signed(juror_4), market_id, vote_item_0.clone(), salt), + Court::reveal_vote( + RuntimeOrigin::signed(juror_4), + market_id, + vote_item_0.clone(), + salt + ), CError::::JurorDidNotVote ); @@ -3360,7 +3380,11 @@ fn simulate_appeal_cycle(market_id: MarketId) { for draw in &draws { let commitment = BlakeTwo256::hash_of(&(draw.court_participant, wrong_vote_item.clone(), salt)); - assert_ok!(Court::vote(RuntimeOrigin::signed(draw.court_participant), market_id, commitment)); + assert_ok!(Court::vote( + RuntimeOrigin::signed(draw.court_participant), + market_id, + commitment + )); } let aggregation_start = court.round_ends.vote + 1; diff --git a/zrml/simple-disputes/Cargo.toml b/zrml/simple-disputes/Cargo.toml index 84eeae085..64e07053e 100644 --- a/zrml/simple-disputes/Cargo.toml +++ b/zrml/simple-disputes/Cargo.toml @@ -2,7 +2,7 @@ frame-benchmarking = { branch = "polkadot-v0.9.32", default-features = false, optional = true, git = "https://github.com/paritytech/substrate" } frame-support = { branch = "polkadot-v0.9.32", default-features = false, git = "https://github.com/paritytech/substrate" } frame-system = { branch = "polkadot-v0.9.32", default-features = false, git = "https://github.com/paritytech/substrate" } -orml-traits = { branch = "polkadot-v0.9.32", git = "https://github.com/open-web3-stack/open-runtime-module-library" } +orml-traits = { branch = "polkadot-v0.9.32", default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library" } parity-scale-codec = { default-features = false, features = ["derive", "max-encoded-len"], version = "3.0.0" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-runtime = { branch = "polkadot-v0.9.32", default-features = false, git = "https://github.com/paritytech/substrate" } From 04795165c3cfd27ce1f89dd8dc6d433b2acf3048 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 5 Jun 2023 11:45:53 +0200 Subject: [PATCH 333/368] correct migrations --- runtime/common/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 3b76c0382..d132b43c6 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -60,6 +60,12 @@ macro_rules! decl_common_types { } } + #[cfg(feature = "with-global-disputes")] + type ConditionalMigration = zrml_global_disputes::migrations::ModifyGlobalDisputesStructures; + + #[cfg(not(feature = "with-global-disputes"))] + type ConditionalMigration = (); + pub type Executive = frame_executive::Executive< Runtime, Block, @@ -68,7 +74,7 @@ macro_rules! decl_common_types { AllPalletsWithSystem, ( zrml_prediction_markets::migrations::AddOutsiderBond, - zrml_global_disputes::migrations::ModifyGlobalDisputesStructures, + ConditionalMigration, ), >; From ae2d4f7918840e3719924d64ca3e30e727b4ce9c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 7 Jul 2023 13:57:20 +0200 Subject: [PATCH 334/368] fix copyrights --- zrml/prediction-markets/src/benchmarks.rs | 2 +- zrml/prediction-markets/src/tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 242e462a1..8f85cd9fe 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Forecasting Technologies Ltd. +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 2931c795f..c9397ae32 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Forecasting Technologies Ltd. +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. From d5593449bb6b6dbe2699c071e36705f09e5035fe Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 7 Jul 2023 13:58:01 +0200 Subject: [PATCH 335/368] fmt --- zrml/global-disputes/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 556906412..a57e739d5 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -99,11 +99,11 @@ mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type DisputeResolution: DisputeResolutionApi< - AccountId = Self::AccountId, - BlockNumber = Self::BlockNumber, - MarketId = MarketIdOf, - Moment = MomentOf, - >; + AccountId = Self::AccountId, + BlockNumber = Self::BlockNumber, + MarketId = MarketIdOf, + Moment = MomentOf, + >; /// The vote lock identifier. #[pallet::constant] @@ -115,10 +115,10 @@ mod pallet { /// To reference the market id type. type MarketCommons: MarketCommonsPalletApi< - AccountId = Self::AccountId, - Currency = Self::Currency, - BlockNumber = Self::BlockNumber, - >; + AccountId = Self::AccountId, + Currency = Self::Currency, + BlockNumber = Self::BlockNumber, + >; /// The maximum numbers of distinct markets /// on which one account can simultaneously vote on outcomes. From be077d8e12099d0210a001f09d1b35efbdb0b7cc Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 7 Jul 2023 16:31:29 +0200 Subject: [PATCH 336/368] fix clippy --- zrml/global-disputes/src/tests.rs | 70 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 2a657baa8..55d6485e4 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -81,12 +81,12 @@ fn add_vote_outcome_works() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); - let free_balance_alice_before = Balances::free_balance(&ALICE); + let free_balance_alice_before = Balances::free_balance(ALICE); let free_balance_reward_account = Balances::free_balance(GlobalDisputes::reward_account(&market_id)); assert_ok!(GlobalDisputes::add_vote_outcome( @@ -119,7 +119,7 @@ fn is_active_works(status: GdStatus) { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); assert!(!GlobalDisputes::is_active(&market_id)); @@ -156,7 +156,7 @@ fn destroy_global_dispute_works() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -175,7 +175,7 @@ fn start_global_dispute_works() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -200,7 +200,7 @@ fn start_global_dispute_fails_if_outcome_mismatch() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = vec![ InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, @@ -225,7 +225,7 @@ fn start_global_dispute_fails_if_less_than_two_outcomes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = vec![InitialItem { outcome: OutcomeReport::Scalar(0), @@ -244,7 +244,7 @@ fn start_global_dispute_fails_if_not_two_unique_outcomes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); // three times the same outcome => not at least two unique outcomes let initial_items = vec![ @@ -264,7 +264,7 @@ fn start_global_dispute_fails_if_already_exists() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()).unwrap(); @@ -403,7 +403,7 @@ fn add_vote_outcome_fails_with_outcome_mismatch() { // create scalar market let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -439,7 +439,7 @@ fn add_vote_outcome_fails_if_no_global_dispute_present() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); assert_noop!( GlobalDisputes::add_vote_outcome( RuntimeOrigin::signed(ALICE), @@ -457,7 +457,7 @@ fn add_vote_outcome_fails_if_global_dispute_is_in_wrong_state(status: GdStatus(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let possession = Possession::Shared { owners: BoundedVec::try_from(vec![ALICE]).unwrap() }; let mut gd_info = GlobalDisputeInfo::new(OutcomeReport::Scalar(0), possession, 10 * BASE); gd_info.status = status; @@ -479,7 +479,7 @@ fn add_vote_outcome_fails_if_outcome_already_exists() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -507,7 +507,7 @@ fn add_vote_outcome_fails_if_balance_too_low() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -672,7 +672,7 @@ fn vote_fails_if_amount_below_min_outcome_vote_amount() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -694,7 +694,7 @@ fn vote_fails_for_insufficient_funds() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -717,7 +717,7 @@ fn determine_voting_winner_sets_the_last_outcome_for_same_vote_balances_as_the_c ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -767,7 +767,7 @@ fn vote_on_outcome_check_event() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -798,7 +798,7 @@ fn reserve_before_init_vote_outcome_is_not_allowed_for_voting() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let disputor = &ALICE; let free_balance_disputor_before = Balances::free_balance(disputor); @@ -859,7 +859,7 @@ fn transfer_fails_with_fully_locked_balance() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -896,7 +896,7 @@ fn reserve_fails_with_fully_locked_balance() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -933,7 +933,7 @@ fn determine_voting_winner_works_four_outcome_votes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -988,7 +988,7 @@ fn determine_voting_winner_works_three_outcome_votes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -1037,7 +1037,7 @@ fn determine_voting_winner_works_two_outcome_votes() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -1086,7 +1086,7 @@ fn determine_voting_winner_works_with_accumulated_votes_for_alice() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -1149,7 +1149,7 @@ fn purge_outcomes_fully_cleaned_works() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -1262,7 +1262,7 @@ fn refund_vote_fees_works() { assert_ok!(GlobalDisputes::destroy_global_dispute(&market_id)); - let alice_free_balance_before = Balances::free_balance(&ALICE); + let alice_free_balance_before = Balances::free_balance(ALICE); assert_ok!(GlobalDisputes::refund_vote_fees(RuntimeOrigin::signed(ALICE), market_id,)); System::assert_last_event(Event::::OutcomesPartiallyCleaned { market_id }.into()); @@ -1276,7 +1276,7 @@ fn refund_vote_fees_works() { assert_eq!(>::iter_prefix(market_id).next(), None); assert_eq!( - Balances::free_balance(&ALICE), + Balances::free_balance(ALICE), alice_free_balance_before.saturating_add(overall_fees) ); }); @@ -1287,7 +1287,7 @@ fn unlock_clears_lock_info() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -1316,7 +1316,7 @@ fn vote_fails_if_outcome_does_not_exist() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = vec![ InitialItem { owner: ALICE, outcome: OutcomeReport::Scalar(0), amount: 10 * BASE }, @@ -1346,7 +1346,7 @@ fn locking_works_for_one_market() { ExtBuilder::default().build().execute_with(|| { let market_id = 0u128; let market = market_mock::(); - Markets::::insert(market_id, &market); + Markets::::insert(market_id, market); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())); @@ -1437,11 +1437,11 @@ fn locking_works_for_two_markets_with_stronger_first_unlock() { ExtBuilder::default().build().execute_with(|| { let market_id_1 = 0u128; let market_1 = market_mock::(); - Markets::::insert(market_id_1, &market_1); + Markets::::insert(market_id_1, market_1); let market_id_2 = 1u128; let market_2 = market_mock::(); - Markets::::insert(market_id_2, &market_2); + Markets::::insert(market_id_2, market_2); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1, initial_items.as_slice())); @@ -1536,11 +1536,11 @@ fn locking_works_for_two_markets_with_weaker_first_unlock() { ExtBuilder::default().build().execute_with(|| { let market_id_1 = 0u128; let market_1 = market_mock::(); - Markets::::insert(market_id_1, &market_1); + Markets::::insert(market_id_1, market_1); let market_id_2 = 1u128; let market_2 = market_mock::(); - Markets::::insert(market_id_2, &market_2); + Markets::::insert(market_id_2, market_2); let initial_items = get_initial_items(); assert_ok!(GlobalDisputes::start_global_dispute(&market_id_1, initial_items.as_slice())); From b84b6ea34fbb1bbc153b5baee425b69a8813b4a4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 8 Aug 2023 13:59:10 +0200 Subject: [PATCH 337/368] fix copyrights --- zrml/court/src/weights.rs | 1 + zrml/prediction-markets/src/benchmarks.rs | 2 +- zrml/prediction-markets/src/tests.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/weights.rs b/zrml/court/src/weights.rs index 9785a0925..faf60e5fe 100644 --- a/zrml/court/src/weights.rs +++ b/zrml/court/src/weights.rs @@ -1,3 +1,4 @@ +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 5f5a3202e..50cfc3e39 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Forecasting Technologies Ltd. +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 00e716e3d..ef76e330d 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Forecasting Technologies Ltd. +// Copyright 2022-2023 Forecasting Technologies LTD. // Copyright 2021-2022 Zeitgeist PM LLC. // // This file is part of Zeitgeist. From 644e583a912ae449ce158ac2e15a50e03c4dd9e3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 09:55:57 +0200 Subject: [PATCH 338/368] update changelog for devs --- docs/changelog_for_devs.md | 72 ++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index ae835f9b9..f3864bf13 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -12,6 +12,44 @@ As of 0.3.9, the changelog's format is based on components which query the chain's storage, the extrinsics or the runtime APIs/RPC interface. +## v0.4.0 +[#976]: https://github.com/zeitgeistpm/zeitgeist/pull/976 + +### Added +- ⚠️ Add court production implementation ([#976]). + Dispatchable calls are: + - `join_court` - Join the court with a stake to become a juror in order to get the stake-weighted + chance to be selected for decision making. + - `delegate` - Join the court with a stake to become a delegator in order to + delegate the voting power to actively participating jurors. + - `prepare_exit_court` - Prepare as a court participant to leave the court system. + - `exit_court` - Exit the court system in order to get the stake back. + - `vote` - An actively participating juror votes secretely on a specific court case, + in which the juror got selected. + - `denounce_vote` - Denounce a selected and active juror, if the secret and + vote is known before the actual reveal period. + - `reveal_vote` - An actively participating juror reveals the previously casted secret vote. + - `appeal` - After the reveal phase (aggregation period), the jurors decision can be appealed. + - `reassign_juror_stakes` - After the appeal period is over, + losers pay the winners for the jurors and delegators. + - `set_inflation` - Set the yearly inflation rate of the court system. + Events are: + - `JurorJoined` - A juror joined the court system. + - `ExitPrepared` - A court participant prepared to exit the court system. + - `ExitedCourt` - A court participant exited the court system. + - `JurorVoted` - A juror voted on a specific court case. + - `JurorRevealedVote` - A juror revealed the previously casted secret vote. + - `DenouncedJurorVote` - A juror was denounced. + - `DelegatorJoined` - A delegator joined the court system. + - `CourtAppealed` - A court case was appealed. + - `MintedInCourt` - A court participant was rewarded with newly minted tokens. + - `StakesReassigned` - The juror and delegator stakes have been reassigned. + The losing jurors have been slashed. + The winning jurors have been rewarded by the losers. + The losing jurors are those, who did not vote, or did not vote with the plurality, + were denounced or did not reveal their vote. + - `InflationSet` - The yearly inflation rate of the court system was set. + ## v0.3.11 [#1049]: https://github.com/zeitgeistpm/zeitgeist/pull/1049 @@ -41,7 +79,6 @@ APIs/RPC interface. ## v0.3.9 -[#976]: https://github.com/zeitgeistpm/zeitgeist/pull/976 [#1011]: https://github.com/zeitgeistpm/zeitgeist/pull/1011 [#937]: https://github.com/zeitgeistpm/zeitgeist/pull/937 [#903]: https://github.com/zeitgeistpm/zeitgeist/pull/903 @@ -50,39 +87,6 @@ APIs/RPC interface. - ⚠️ Add `outsider` field to `MarketBonds` struct. In particular, the `Market` struct's layout has changed ([#903]). -- ⚠️ Add court production implementation ([#976]). - Dispatchable calls are: - - `join_court` - Join the court with a stake to become a juror in order to get the stake-weighted - chance to be selected for decision making. - - `delegate` - Join the court with a stake to become a delegator in order to - delegate the voting power to actively participating jurors. - - `prepare_exit_court` - Prepare as a court participant to leave the court system. - - `exit_court` - Exit the court system in order to get the stake back. - - `vote` - An actively participating juror votes secretely on a specific court case, - in which the juror got selected. - - `denounce_vote` - Denounce a selected and active juror, if the secret and - vote is known before the actual reveal period. - - `reveal_vote` - An actively participating juror reveals the previously casted secret vote. - - `appeal` - After the reveal phase (aggregation period), the jurors decision can be appealed. - - `reassign_juror_stakes` - After the appeal period is over, - losers pay the winners for the jurors and delegators. - - `set_inflation` - Set the yearly inflation rate of the court system. - Events are: - - `JurorJoined` - A juror joined the court system. - - `ExitPrepared` - A court participant prepared to exit the court system. - - `ExitedCourt` - A court participant exited the court system. - - `JurorVoted` - A juror voted on a specific court case. - - `JurorRevealedVote` - A juror revealed the previously casted secret vote. - - `DenouncedJurorVote` - A juror was denounced. - - `DelegatorJoined` - A delegator joined the court system. - - `CourtAppealed` - A court case was appealed. - - `MintedInCourt` - A court participant was rewarded with newly minted tokens. - - `StakesReassigned` - The juror and delegator stakes have been reassigned. - The losing jurors have been slashed. - The winning jurors have been rewarded by the losers. - The losing jurors are those, who did not vote, or did not vote with the plurality, - were denounced or did not reveal their vote. - - `InflationSet` - The yearly inflation rate of the court system was set. - Adjust `deposit` function used to calculate storage fees for the following pallets: identity, multisig, preimage, proxy. The cost of adding an identity reduced from a minimum of 125 ZTG to a minimum of 1.5243 ZTG ([#1011]) From 3d70ec892032dfca68f83b31651b5eff6e6670ff Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 10:22:45 +0200 Subject: [PATCH 339/368] Update runtime/common/src/lib.rs Co-authored-by: Harald Heckmann --- runtime/common/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 512eac275..1f89ac289 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -62,7 +62,6 @@ macro_rules! decl_common_types { zrml_prediction_markets::migrations::MoveDataToSimpleDisputes, ); - // TODO: Reconsider the migrations here. Why AddOutsiderAndDisputeBond and AddOutsiderBond? Only one AddOutsiderAndDisputeBond enough? pub type Executive = frame_executive::Executive< Runtime, Block, From 6f44b921b73fe538fd2b273bbdb2c71e0e6edfe5 Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 10:36:30 +0200 Subject: [PATCH 340/368] Update zrml/court/src/lib.rs Co-authored-by: Harald Heckmann --- zrml/court/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bc02c69d9..be664f6b7 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -983,8 +983,7 @@ mod pallet { /// /// # Weight /// - /// Complexity: It depends heavily on the number of delegations - /// and the number of selected draws. + /// Complexity: O(N + M), with `N` being the number of draws and `M` being the total number of valid winners and losers. #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::reassign_court_stakes(T::MaxSelectedDraws::get()))] #[transactional] From 5c8bde25325cc4742f8845d3da62f9aee07f578d Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 10:54:01 +0200 Subject: [PATCH 341/368] Update zrml/court/src/benchmarks.rs Co-authored-by: Harald Heckmann --- zrml/court/src/benchmarks.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index f46b34f21..14873a42f 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -86,7 +86,10 @@ fn deposit(caller: &T::AccountId) where T: Config, { - let _ = T::Currency::deposit_creating(caller, BalanceOf::::max_value()); + let _ = T::Currency::deposit_creating( + caller, + BalanceOf::::max_value() / BalanceOf::::from(2u8), + ); } fn fill_pool(number: u32) -> Result<(), &'static str> From b11b188af4c87375d769ccc0a109b2ec86cb127e Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 10:59:43 +0200 Subject: [PATCH 342/368] comment benchmark components --- zrml/court/src/benchmarks.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 14873a42f..7306815dc 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -422,7 +422,9 @@ benchmarks! { // from 255 because in the last appeal round we need at least 255 jurors let j in 255..T::MaxCourtParticipants::get(); let a in 0..(T::MaxAppeals::get() - 2); + // the number of market ids inside MarketIdsPerCloseBlock at the old appeal end block let r in 0..62; + // the number of market ids inside MarketIdsPerCloseBlock at the new appeal end block let f in 0..62; let necessary_draws_weight = Court::::necessary_draws_weight((T::MaxAppeals::get() - 1) as usize); From 5ab68a1c286fb944df68889630f2a064237c4b87 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 11:09:38 +0200 Subject: [PATCH 343/368] comment benchmark component --- zrml/court/src/benchmarks.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zrml/court/src/benchmarks.rs b/zrml/court/src/benchmarks.rs index 7306815dc..d18860390 100644 --- a/zrml/court/src/benchmarks.rs +++ b/zrml/court/src/benchmarks.rs @@ -586,6 +586,7 @@ benchmarks! { on_dispute { let j in 31..T::MaxCourtParticipants::get(); + // the number of market ids inside MarketIdsPerCloseBlock at the appeal end block let r in 0..62; let now = >::block_number(); From ee75b0ae0ebcffaaa1d6b95fc9e4d330e508154c Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 11:10:29 +0200 Subject: [PATCH 344/368] Update zrml/court/README.md Co-authored-by: Harald Heckmann --- zrml/court/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index 4af7b7704..27ab93d2c 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -9,7 +9,7 @@ A pallet for stake-weighted plurality decision making. ## Overview -Court is a market dispute resolution mechanism. It allows jurors to make decisions. +Court is a market dispute resolution mechanism. It allows jurors to discover the truth. If a juror does not vote with the plurality of the other jurors, the juror will be punished, while those who did vote with the plurality will be rewarded. From 502a60def7fb6de1893f076e85e63235dac1e5b7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 11:16:25 +0200 Subject: [PATCH 345/368] add terminology for court --- zrml/court/README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/zrml/court/README.md b/zrml/court/README.md index 4af7b7704..3dfabbee7 100644 --- a/zrml/court/README.md +++ b/zrml/court/README.md @@ -9,14 +9,24 @@ A pallet for stake-weighted plurality decision making. ## Overview -Court is a market dispute resolution mechanism. It allows jurors to make decisions. -If a juror does not vote with the plurality of the other jurors, the juror will be punished, -while those who did vote with the plurality will be rewarded. +Court is a market dispute resolution mechanism. It allows jurors to make +decisions. If a juror does not vote with the plurality of the other jurors, the +juror will be punished, while those who did vote with the plurality will be +rewarded. ## Terminology - **Aggregation Period:** The period in which the actively participating jurors need to reveal their vote secrets. +- **Appeal Period:** The period in which the jurors can appeal the decision of + the last court round. +- **Court:** The court is a dispute resolution mechanism to find the resolution + outcome of a market. +- **Delegator:** A delegator is a court participant who delegates their voting + power to an actively participating juror. +- **Juror:** A juror is a court participant who votes inside court cases. +- **Reveal Period / Aggregation Period:** The period in which the actively + participating jurors need to reveal their vote secrets. ## Interface From 292deed9c8d9b42a823e454acbca493f6e3af2ed Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 11:17:54 +0200 Subject: [PATCH 346/368] Update zrml/prediction-markets/src/benchmarks.rs Co-authored-by: Harald Heckmann --- zrml/prediction-markets/src/benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 50cfc3e39..73dc01d9d 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -818,7 +818,7 @@ benchmarks! { ::AssetManager::deposit( Asset::Ztg, &juror, - u128::MAX.saturated_into(), + (u128::MAX / 2).saturated_into(), ).unwrap(); >::join_court( RawOrigin::Signed(juror.clone()).into(), From 4ddbac0478bec138a14737ce14459e4bb7d616ef Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 11:31:58 +0200 Subject: [PATCH 347/368] restructure import --- zrml/prediction-markets/src/benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 73dc01d9d..e5758b9c7 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -46,6 +46,7 @@ use zeitgeist_primitives::{ }; use zrml_authorized::Pallet as AuthorizedPallet; use zrml_market_commons::MarketCommonsPalletApi; +use zeitgeist_primitives::traits::DisputeApi; use frame_support::{traits::Hooks, BoundedVec}; @@ -838,7 +839,6 @@ benchmarks! { .dispatch_bypass_filter(RawOrigin::Signed(disputor).into())?; let market = >::market(&market_id.saturated_into()).unwrap(); - use zeitgeist_primitives::traits::DisputeApi; let appeal_end = T::Court::get_auto_resolve(&market_id, &market).result.unwrap(); let mut market_ids_2: BoundedVec, CacheSize> = BoundedVec::try_from( vec![market_id], From b51db9dbcb7ada3ef241bce7787887665aa93fa5 Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 11:33:19 +0200 Subject: [PATCH 348/368] Update zrml/court/src/lib.rs Co-authored-by: Harald Heckmann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index be664f6b7..06f3f6f98 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -192,7 +192,7 @@ mod pallet { type WeightInfo: WeightInfoZeitgeist; } - // Number of draws for the initial court round. + /// Number of draws for the initial court round. const INITIAL_DRAWS_NUM: usize = 31; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); From 9446d344d105dc70519a027fce33e331d57940aa Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 11:33:47 +0200 Subject: [PATCH 349/368] Update zrml/court/src/lib.rs Co-authored-by: Harald Heckmann --- zrml/court/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 06f3f6f98..1f5d5042a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -196,8 +196,8 @@ mod pallet { const INITIAL_DRAWS_NUM: usize = 31; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); - // Weight used to increase the number of jurors for subsequent appeals - // of the same court. + /// Weight used to increase the number of jurors for subsequent appeals + /// of the same court. const APPEAL_BASIS: usize = 2; // Basis used to increase the bond for subsequent appeals of the same market. const APPEAL_BOND_BASIS: u32 = 2; From 9d8b87be7420770effb424cae7f432fdecc36816 Mon Sep 17 00:00:00 2001 From: Chralt Date: Mon, 21 Aug 2023 11:34:11 +0200 Subject: [PATCH 350/368] Update zrml/court/src/lib.rs Co-authored-by: Harald Heckmann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 1f5d5042a..bf3dc0303 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -199,7 +199,7 @@ mod pallet { /// Weight used to increase the number of jurors for subsequent appeals /// of the same court. const APPEAL_BASIS: usize = 2; - // Basis used to increase the bond for subsequent appeals of the same market. + /// Basis used to increase the bond for subsequent appeals of the same market. const APPEAL_BOND_BASIS: u32 = 2; pub(crate) type AccountIdOf = ::AccountId; From 9715890e822cd6e79ae4bdd7f9229e9160bb4cda Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 11:53:10 +0200 Subject: [PATCH 351/368] use mul_floor and reduce indentation --- zrml/court/src/lib.rs | 85 ++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index be664f6b7..dc5ff31ea 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1232,53 +1232,54 @@ mod pallet { // Handle the external incentivisation of the court system. pub(crate) fn handle_inflation(now: T::BlockNumber) -> Weight { let inflation_period = T::InflationPeriod::get(); - if (now % inflation_period).is_zero() { - let yearly_inflation_rate = >::get(); - let yearly_inflation_amount = yearly_inflation_rate * T::Currency::total_issuance(); - let blocks_per_year = T::BlocksPerYear::get() - .saturated_into::() - .saturated_into::>(); - debug_assert!(!T::BlocksPerYear::get().is_zero()); - let issue_per_block = yearly_inflation_amount / blocks_per_year.max(One::one()); - - let inflation_period_mint = issue_per_block.saturating_mul( - inflation_period.saturated_into::().saturated_into::>(), - ); + if !(now % inflation_period).is_zero() { + return Weight::zero(); + } + + let yearly_inflation_rate = >::get(); + let yearly_inflation_amount = + yearly_inflation_rate.mul_floor(T::Currency::total_issuance()); + let blocks_per_year = T::BlocksPerYear::get() + .saturated_into::() + .saturated_into::>(); + debug_assert!(!T::BlocksPerYear::get().is_zero()); + let issue_per_block = yearly_inflation_amount / blocks_per_year.max(One::one()); + + let inflation_period_mint = issue_per_block.saturating_mul( + inflation_period.saturated_into::().saturated_into::>(), + ); - let pool = >::get(); - let pool_len = pool.len() as u32; - let at_least_one_inflation_period = - |joined_at| now.saturating_sub(joined_at) >= T::InflationPeriod::get(); - let total_stake = pool - .iter() - .filter(|pool_item| at_least_one_inflation_period(pool_item.joined_at)) - .fold(0u128, |acc, pool_item| { - acc.saturating_add(pool_item.stake.saturated_into::()) + let pool = >::get(); + let pool_len = pool.len() as u32; + let at_least_one_inflation_period = + |joined_at| now.saturating_sub(joined_at) >= T::InflationPeriod::get(); + let total_stake = pool + .iter() + .filter(|pool_item| at_least_one_inflation_period(pool_item.joined_at)) + .fold(0u128, |acc, pool_item| { + acc.saturating_add(pool_item.stake.saturated_into::()) + }); + for CourtPoolItem { stake, court_participant, joined_at, .. } in pool { + if !at_least_one_inflation_period(joined_at) { + // participants who joined and didn't wait + // at least one full inflation period won't get a reward + continue; + } + let share = + Perquintill::from_rational(stake.saturated_into::(), total_stake); + let mint = share.mul_floor(inflation_period_mint.saturated_into::()); + if let Ok(imb) = T::Currency::deposit_into_existing( + &court_participant, + mint.saturated_into::>(), + ) { + Self::deposit_event(Event::MintedInCourt { + court_participant: court_participant.clone(), + amount: imb.peek(), }); - for CourtPoolItem { stake, court_participant, joined_at, .. } in pool { - if !at_least_one_inflation_period(joined_at) { - // participants who joined and didn't wait - // at least one full inflation period won't get a reward - continue; - } - let share = - Perquintill::from_rational(stake.saturated_into::(), total_stake); - let mint = share * inflation_period_mint.saturated_into::(); - if let Ok(imb) = T::Currency::deposit_into_existing( - &court_participant, - mint.saturated_into::>(), - ) { - Self::deposit_event(Event::MintedInCourt { - court_participant: court_participant.clone(), - amount: imb.peek(), - }); - } } - - return T::WeightInfo::handle_inflation(pool_len); } - Weight::zero() + return T::WeightInfo::handle_inflation(pool_len); } // Get `n` unique and ordered random `MinJurorStake` section ends From e471c1e267bd8fd8d2d54cd3c70e6f4794f057f1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 14:38:21 +0200 Subject: [PATCH 352/368] safe guard inflation emission --- primitives/src/constants/mock.rs | 2 +- zrml/court/src/lib.rs | 26 +++++++++++++++-------- zrml/court/src/tests.rs | 10 ++++----- zrml/prediction-markets/src/benchmarks.rs | 3 +-- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/primitives/src/constants/mock.rs b/primitives/src/constants/mock.rs index 4c92e4fc3..29a597379 100644 --- a/primitives/src/constants/mock.rs +++ b/primitives/src/constants/mock.rs @@ -36,7 +36,7 @@ parameter_types! { parameter_types! { pub const AppealBond: Balance = 5 * BASE; pub const AppealBondFactor: Balance = 2 * BASE; - pub const BlocksPerYear: BlockNumber = 100; + pub const BlocksPerYear: BlockNumber = 10000; pub const CourtPalletId: PalletId = PalletId(*b"zge/cout"); pub const RequestInterval: BlockNumber = 15; pub const VotePeriod: BlockNumber = 3; diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 77b1fbdfd..8cf131a5d 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1235,20 +1235,29 @@ mod pallet { if !(now % inflation_period).is_zero() { return Weight::zero(); } - + let yearly_inflation_rate = >::get(); - let yearly_inflation_amount = - yearly_inflation_rate.mul_floor(T::Currency::total_issuance()); - let blocks_per_year = T::BlocksPerYear::get() - .saturated_into::() - .saturated_into::>(); + // example: 1049272791644671442 + let total_supply = T::Currency::total_issuance(); + // example: 0.02 * 1049272791644671442 = 20985455832893428 + let yearly_inflation_amount = yearly_inflation_rate.mul_floor(total_supply); + let blocks_per_year = + T::BlocksPerYear::get().saturated_into::().saturated_into::>(); debug_assert!(!T::BlocksPerYear::get().is_zero()); + // example: 20985455832893428 / 2629800 = 7979867607 let issue_per_block = yearly_inflation_amount / blocks_per_year.max(One::one()); + // example: 7979867607 * 7200 * 30 = 1723651403112000 let inflation_period_mint = issue_per_block.saturating_mul( inflation_period.saturated_into::().saturated_into::>(), ); + // safe guard: inflation per period should never exceed the yearly inflation amount + if inflation_period_mint > yearly_inflation_amount { + debug_assert!(false); + return Weight::zero(); + } + let pool = >::get(); let pool_len = pool.len() as u32; let at_least_one_inflation_period = @@ -1265,8 +1274,7 @@ mod pallet { // at least one full inflation period won't get a reward continue; } - let share = - Perquintill::from_rational(stake.saturated_into::(), total_stake); + let share = Perquintill::from_rational(stake.saturated_into::(), total_stake); let mint = share.mul_floor(inflation_period_mint.saturated_into::()); if let Ok(imb) = T::Currency::deposit_into_existing( &court_participant, @@ -1279,7 +1287,7 @@ mod pallet { } } - return T::WeightInfo::handle_inflation(pool_len); + T::WeightInfo::handle_inflation(pool_len) } // Get `n` unique and ordered random `MinJurorStake` section ends diff --git a/zrml/court/src/tests.rs b/zrml/court/src/tests.rs index f3fa0ca98..d98cbfe63 100644 --- a/zrml/court/src/tests.rs +++ b/zrml/court/src/tests.rs @@ -3062,19 +3062,19 @@ fn handle_inflation_works() { Court::handle_inflation(now); let free_balance_after_0 = Balances::free_balance(jurors_list[0]); - assert_eq!(free_balance_after_0 - free_balances_before[&jurors_list[0]], 43_286_841); + assert_eq!(free_balance_after_0 - free_balances_before[&jurors_list[0]], 432_012); let free_balance_after_1 = Balances::free_balance(jurors_list[1]); - assert_eq!(free_balance_after_1 - free_balances_before[&jurors_list[1]], 432_868_409); + assert_eq!(free_balance_after_1 - free_balances_before[&jurors_list[1]], 4_320_129); let free_balance_after_2 = Balances::free_balance(jurors_list[2]); - assert_eq!(free_balance_after_2 - free_balances_before[&jurors_list[2]], 4_328_684_088); + assert_eq!(free_balance_after_2 - free_balances_before[&jurors_list[2]], 43_201_302); let free_balance_after_3 = Balances::free_balance(jurors_list[3]); - assert_eq!(free_balance_after_3 - free_balances_before[&jurors_list[3]], 43_286_840_884); + assert_eq!(free_balance_after_3 - free_balances_before[&jurors_list[3]], 432_013_038); let free_balance_after_4 = Balances::free_balance(jurors_list[4]); - assert_eq!(free_balance_after_4 - free_balances_before[&jurors_list[4]], 432_868_408_838); + assert_eq!(free_balance_after_4 - free_balances_before[&jurors_list[4]], 4_320_130_393); }); } diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index e5758b9c7..ba92228c5 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -37,7 +37,7 @@ use orml_traits::MultiCurrency; use sp_runtime::traits::{One, SaturatedConversion, Saturating, Zero}; use zeitgeist_primitives::{ constants::mock::{MaxSwapFee, MinWeight, BASE, MILLISECS_PER_BLOCK}, - traits::Swaps, + traits::{DisputeApi, Swaps}, types::{ Asset, Deadlines, MarketCreation, MarketDisputeMechanism, MarketPeriod, MarketStatus, MarketType, MaxRuntimeUsize, MultiHash, OutcomeReport, PoolStatus, ScoringRule, @@ -46,7 +46,6 @@ use zeitgeist_primitives::{ }; use zrml_authorized::Pallet as AuthorizedPallet; use zrml_market_commons::MarketCommonsPalletApi; -use zeitgeist_primitives::traits::DisputeApi; use frame_support::{traits::Hooks, BoundedVec}; From 9a7163858b3cd5eaa886ce343bddc878288c82c7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 15:41:54 +0200 Subject: [PATCH 353/368] use imbalance for minting --- zrml/court/src/lib.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 8cf131a5d..de3a77209 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1258,6 +1258,8 @@ mod pallet { return Weight::zero(); } + let mut total_mint = T::Currency::issue(inflation_period_mint); + let pool = >::get(); let pool_len = pool.len() as u32; let at_least_one_inflation_period = @@ -1276,13 +1278,13 @@ mod pallet { } let share = Perquintill::from_rational(stake.saturated_into::(), total_stake); let mint = share.mul_floor(inflation_period_mint.saturated_into::()); - if let Ok(imb) = T::Currency::deposit_into_existing( - &court_participant, - mint.saturated_into::>(), - ) { + let (mint_imb, remainder) = total_mint.split(mint.saturated_into::>()); + let mint_amount = mint_imb.peek(); + total_mint = remainder; + if let Ok(()) = T::Currency::resolve_into_existing(&court_participant, mint_imb) { Self::deposit_event(Event::MintedInCourt { court_participant: court_participant.clone(), - amount: imb.peek(), + amount: mint_amount, }); } } From 53dc51ce5082980011ba566381da9d60014ca44c Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 21 Aug 2023 16:00:35 +0200 Subject: [PATCH 354/368] use saturated div --- zrml/court/src/lib.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index de3a77209..5d01c92cb 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1240,17 +1240,21 @@ mod pallet { // example: 1049272791644671442 let total_supply = T::Currency::total_issuance(); // example: 0.02 * 1049272791644671442 = 20985455832893428 - let yearly_inflation_amount = yearly_inflation_rate.mul_floor(total_supply); - let blocks_per_year = - T::BlocksPerYear::get().saturated_into::().saturated_into::>(); + let yearly_inflation_amount = + yearly_inflation_rate.mul_floor(total_supply).saturated_into::(); + let blocks_per_year = T::BlocksPerYear::get().saturated_into::(); debug_assert!(!T::BlocksPerYear::get().is_zero()); // example: 20985455832893428 / 2629800 = 7979867607 - let issue_per_block = yearly_inflation_amount / blocks_per_year.max(One::one()); + let issue_per_block = + yearly_inflation_amount.saturating_div(blocks_per_year.max(One::one())); + + let yearly_inflation_amount = yearly_inflation_amount.saturated_into::>(); + let issue_per_block = issue_per_block.saturated_into::>(); + let inflation_period = + inflation_period.saturated_into::().saturated_into::>(); // example: 7979867607 * 7200 * 30 = 1723651403112000 - let inflation_period_mint = issue_per_block.saturating_mul( - inflation_period.saturated_into::().saturated_into::>(), - ); + let inflation_period_mint = issue_per_block.saturating_mul(inflation_period); // safe guard: inflation per period should never exceed the yearly inflation amount if inflation_period_mint > yearly_inflation_amount { From 4dd09310c5aaf85af80f21e48f61ad70a7f96f45 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 22 Aug 2023 09:33:39 +0200 Subject: [PATCH 355/368] logging if inflation period mint too high --- zrml/court/src/lib.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 5d01c92cb..ff9dad44a 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1247,7 +1247,7 @@ mod pallet { // example: 20985455832893428 / 2629800 = 7979867607 let issue_per_block = yearly_inflation_amount.saturating_div(blocks_per_year.max(One::one())); - + let yearly_inflation_amount = yearly_inflation_amount.saturated_into::>(); let issue_per_block = issue_per_block.saturated_into::>(); let inflation_period = @@ -1256,6 +1256,21 @@ mod pallet { // example: 7979867607 * 7200 * 30 = 1723651403112000 let inflation_period_mint = issue_per_block.saturating_mul(inflation_period); + // inflation_period_mint shouldn't exceed 0.5% of the total issuance + let log_threshold = Perbill::from_perthousand(5u32) + .mul_floor(total_supply) + .saturated_into::() + .saturated_into::>(); + if inflation_period_mint >= log_threshold { + log::error!( + "Inflation per period is greater than the threshold. Inflation per period: \ + {:?}, threshold: {:?}", + inflation_period_mint, + log_threshold + ); + debug_assert!(false); + } + // safe guard: inflation per period should never exceed the yearly inflation amount if inflation_period_mint > yearly_inflation_amount { debug_assert!(false); From 77b88253fa88ac02cfc3e187ba6a52d024680e08 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 22 Aug 2023 17:07:49 +0200 Subject: [PATCH 356/368] fix after merge --- Cargo.lock | 1728 ++++++++++++--------- zrml/court/src/lib.rs | 71 +- zrml/global-disputes/src/lib.rs | 19 +- zrml/global-disputes/src/mock.rs | 12 +- zrml/global-disputes/src/tests.rs | 39 - zrml/prediction-markets/src/benchmarks.rs | 1 - zrml/prediction-markets/src/lib.rs | 16 +- zrml/prediction-markets/src/tests.rs | 96 +- 8 files changed, 1046 insertions(+), 936 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 217fd4fc2..4bea22628 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,11 +23,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ - "gimli 0.27.1", + "gimli 0.27.3", ] [[package]] @@ -42,7 +42,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -51,7 +51,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", ] @@ -62,7 +62,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -153,25 +153,31 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" dependencies = [ "memchr", ] [[package]] name = "always-assert" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf688625d06217d5b1bb0ea9d9c44a1635fd0ee3534466388d18203174f4d11" +checksum = "4436e0292ab1bb631b42973c61205e704475fe8126af845c8d923c0996328127" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" @@ -191,11 +197,60 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "approx" @@ -235,9 +290,9 @@ checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -247,9 +302,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "asn1-rs" @@ -291,7 +346,7 @@ checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -303,7 +358,7 @@ checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -315,14 +370,14 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "asn1_der" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" [[package]] name = "assert_matches" @@ -332,63 +387,62 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-io" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg", + "cfg-if", "concurrent-queue", "futures-lite", - "libc", "log", "parking", "polling", + "rustix 0.37.23", "slab", - "socket2", + "socket2 0.4.9", "waker-fn", - "winapi", ] [[package]] name = "async-lock" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener", - "futures-lite", ] [[package]] name = "async-trait" -version = "0.1.63" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] name = "asynchronous-codec" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" dependencies = [ "bytes", "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", ] [[package]] name = "atomic-waker" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" @@ -409,16 +463,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "addr2line 0.19.0", + "addr2line 0.20.0", "cc", "cfg-if", "libc", - "miniz_oxide 0.6.2", - "object 0.30.3", + "miniz_oxide", + "object 0.31.1", "rustc-demangle", ] @@ -448,15 +502,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "battery-station-runtime" @@ -642,11 +696,11 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.60.1" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -657,6 +711,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", + "syn 1.0.109", ] [[package]] @@ -665,6 +720,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bitvec" version = "1.0.1" @@ -683,43 +744,43 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", - "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "arrayvec 0.7.4", + "constant_time_eq 0.2.6", ] [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", - "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "arrayvec 0.7.4", + "constant_time_eq 0.2.6", ] [[package]] name = "blake3" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "895adc16c8b3273fbbc32685a7d55227705eda08c01e77704020f3491924b44b" +checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "cc", "cfg-if", - "constant_time_eq 0.2.4", - "digest 0.10.6", + "constant_time_eq 0.3.0", + "digest 0.10.7", ] [[package]] @@ -740,16 +801,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -794,9 +855,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.1.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", @@ -813,9 +874,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-slice-cast" @@ -837,9 +898,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bzip2-sys" @@ -852,26 +913,20 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "camino" -version = "1.1.2" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" dependencies = [ "serde", ] @@ -884,18 +939,19 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.18", "serde", "serde_json", ] [[package]] name = "cc" -version = "1.0.78" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -966,15 +1022,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi", ] @@ -998,7 +1054,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1007,7 +1063,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1031,9 +1087,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -1042,46 +1098,50 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "coarsetime" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "454038500439e141804c655b4cd1bc6a70bcb95cd2bc9463af5661b6956f0e46" +checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" dependencies = [ "libc", "once_cell", @@ -1099,11 +1159,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "comfy-table" -version = "6.1.4" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7b787b0dc42e8111badfdbe4c3059158ccb2db8780352fa1b01e8ccf45cc4d" +checksum = "7e959d788268e3bf9d35ace83e81b124190378e4c91c9067524675e33394b8ba" dependencies = [ "strum", "strum_macros", @@ -1148,30 +1214,30 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ - "cache-padded", + "crossbeam-utils", ] [[package]] name = "const-oid" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "convert_case" @@ -1191,9 +1257,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core2" @@ -1247,7 +1313,7 @@ version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fed94c8770dc25d01154c3ffa64ed0b3ba9d583736f305fed7beebe5d9cf74" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", @@ -1356,9 +1422,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1366,9 +1432,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -1377,14 +1443,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset 0.9.0", "scopeguard", ] @@ -1400,9 +1466,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -1419,7 +1485,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1431,7 +1497,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum 1.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1442,7 +1508,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -1452,7 +1518,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -1685,7 +1751,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1945,9 +2011,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.3" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" +checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" dependencies = [ "cfg-if", "cpufeatures", @@ -1967,14 +2033,14 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] name = "cxx" -version = "1.0.87" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e" +checksum = "28403c86fc49e3401fdf45499ba37fad6493d9329449d6449d7f0e10f4654d28" dependencies = [ "cc", "cxxbridge-flags", @@ -1984,9 +2050,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.87" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200" +checksum = "78da94fef01786dc3e0c76eafcd187abcaa9972c78e05ff4041e24fdf059c285" dependencies = [ "cc", "codespan-reporting", @@ -1994,24 +2060,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] name = "cxxbridge-flags" -version = "1.0.87" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea" +checksum = "e2a6f5e1dfb4b34292ad4ea1facbfdaa1824705b231610087b00b17008641809" [[package]] name = "cxxbridge-macro" -version = "1.0.87" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e" +checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] @@ -2035,7 +2101,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2046,20 +2112,20 @@ checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "data-encoding-macro" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2067,12 +2133,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" dependencies = [ "data-encoding", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2116,9 +2182,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" [[package]] name = "derivative" @@ -2128,7 +2194,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2139,7 +2205,7 @@ checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -2160,7 +2226,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2170,7 +2236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" dependencies = [ "derive_builder_core", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2183,7 +2249,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2207,16 +2273,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", "subtle", ] @@ -2270,7 +2336,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -2287,9 +2353,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00704156a7de8df8da0911424e30c2049957b0a714542a44e05fe693dd85313" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dyn-clonable" @@ -2309,14 +2375,14 @@ checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" [[package]] name = "ecdsa" @@ -2360,7 +2426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek 3.2.0", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "sha2 0.9.9", @@ -2369,9 +2435,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -2382,9 +2448,9 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "digest 0.10.6", + "digest 0.10.7", "ff", - "generic-array 0.14.6", + "generic-array 0.14.7", "group", "hkdf", "pem-rfc7468", @@ -2404,38 +2470,38 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "enumflags2" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" +checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] name = "enumn" -version = "0.1.6" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" +checksum = "b893c4eb2dc092c811165f84dc7447fae16fb66521717968c34c509b39b1a5c5" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] @@ -2470,6 +2536,12 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.2.8" @@ -2481,6 +2553,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -2528,7 +2611,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2545,13 +2628,19 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fatality" version = "0.0.6" @@ -2569,11 +2658,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" dependencies = [ "expander 0.0.4", - "indexmap", + "indexmap 1.9.3", "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "thiserror", ] @@ -2614,21 +2703,21 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", ] [[package]] name = "finality-grandpa" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24e6c429951433ccb7c87fd528c60084834dcd14763182c1f83291bcde24c34" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" dependencies = [ "either", "futures 0.3.28", @@ -2660,13 +2749,13 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "libz-sys", - "miniz_oxide 0.5.4", + "miniz_oxide", ] [[package]] @@ -2694,9 +2783,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -2785,7 +2874,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2824,9 +2913,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" dependencies = [ "cfg-if", "parity-scale-codec", @@ -2855,7 +2944,7 @@ name = "frame-support" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6fa7fe1326ecaab9921c2c3888530ad679cfbb87" dependencies = [ - "bitflags", + "bitflags 1.3.2", "frame-metadata", "frame-support-procedural", "impl-trait-for-tuples", @@ -2893,7 +2982,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2905,7 +2994,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2915,7 +3004,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6f dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2988,12 +3077,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - [[package]] name = "funty" version = "2.0.0" @@ -3057,16 +3140,16 @@ checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", "parking", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", "waker-fn", ] @@ -3078,7 +3161,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -3124,7 +3207,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", "pin-utils", "slab", ] @@ -3149,9 +3232,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum 1.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "version_check", @@ -3180,9 +3263,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -3216,15 +3299,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] [[package]] name = "gimli" -version = "0.27.1" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "glob" @@ -3234,9 +3317,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ "aho-corasick", "bstr", @@ -3258,9 +3341,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -3268,7 +3351,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -3277,9 +3360,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" dependencies = [ "log", "pest", @@ -3313,11 +3396,17 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -3330,12 +3419,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -3384,7 +3470,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3394,7 +3480,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -3411,9 +3497,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -3428,7 +3514,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", ] [[package]] @@ -3445,9 +3531,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -3457,9 +3543,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -3471,8 +3557,8 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.9", - "socket2", + "pin-project-lite 0.2.12", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -3496,26 +3582,25 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.48.0", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -3537,9 +3622,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -3571,7 +3656,7 @@ dependencies = [ "rtnetlink", "system-configuration", "tokio", - "windows", + "windows 0.34.0", ] [[package]] @@ -3600,20 +3685,30 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "indexmap-nostd" version = "0.4.0" @@ -3626,7 +3721,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -3680,12 +3775,13 @@ checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ + "hermit-abi 0.3.2", "libc", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -3696,32 +3792,31 @@ checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" [[package]] name = "ipconfig" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.3", "widestring", - "winapi", + "windows-sys 0.48.0", "winreg", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes 1.0.4", - "rustix 0.36.7", - "windows-sys 0.42.0", + "hermit-abi 0.3.2", + "rustix 0.38.8", + "windows-sys 0.48.0", ] [[package]] @@ -3735,24 +3830,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -3799,7 +3894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" dependencies = [ "anyhow", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "async-lock", "async-trait", "beef", @@ -3830,7 +3925,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3890,14 +3985,14 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -4060,15 +4155,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libfuzzer-sys" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" dependencies = [ "arbitrary", "cc", @@ -4087,9 +4182,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libp2p" @@ -4100,7 +4195,7 @@ dependencies = [ "bytes", "futures 0.3.28", "futures-timer", - "getrandom 0.2.8", + "getrandom 0.2.10", "instant", "libp2p-core 0.38.0", "libp2p-dns", @@ -4151,7 +4246,7 @@ dependencies = [ "rand 0.8.5", "rw-stream-sink", "sec1", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "thiserror", "unsigned-varint", @@ -4235,7 +4330,7 @@ dependencies = [ "multihash 0.17.0", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "zeroize", ] @@ -4246,7 +4341,7 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "asynchronous-codec", "bytes", "either", @@ -4260,7 +4355,7 @@ dependencies = [ "prost", "prost-build", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "thiserror", "uint", @@ -4282,7 +4377,7 @@ dependencies = [ "log", "rand 0.8.5", "smallvec", - "socket2", + "socket2 0.4.9", "tokio", "trust-dns-proto", "void", @@ -4335,7 +4430,7 @@ dependencies = [ "prost", "prost-build", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "snow", "static_assertions", "thiserror", @@ -4428,7 +4523,7 @@ checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ "heck", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4443,7 +4538,7 @@ dependencies = [ "libc", "libp2p-core 0.38.0", "log", - "socket2", + "socket2 0.4.9", "tokio", ] @@ -4546,9 +4641,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.8.0+7.4.4" +version = "0.8.3+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" +checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" dependencies = [ "bindgen", "bzip2-sys", @@ -4609,9 +4704,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "pkg-config", @@ -4620,9 +4715,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" dependencies = [ "cc", ] @@ -4660,15 +4755,21 @@ checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -4676,9 +4777,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" @@ -4686,7 +4787,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -4739,7 +4840,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -4750,10 +4851,11 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" -version = "0.3.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" dependencies = [ + "autocfg", "rawpointer", ] @@ -4763,7 +4865,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4774,18 +4876,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memfd" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.36.7", + "rustix 0.37.23", ] [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -4801,9 +4903,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -4815,7 +4917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -4855,32 +4957,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -4920,9 +5012,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if", "downcast", @@ -4935,14 +5027,14 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5026,9 +5118,9 @@ dependencies = [ "blake2s_simd", "blake3", "core2", - "digest 0.10.6", + "digest 0.10.7", "multihash-derive", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "unsigned-varint", ] @@ -5054,7 +5146,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -5104,7 +5196,7 @@ checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5141,7 +5233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", "netlink-packet-core", @@ -5150,9 +5242,9 @@ dependencies = [ [[package]] name = "netlink-packet-utils" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", @@ -5177,9 +5269,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e21fbb6f3d253a14df90eb0000a6066780a15dd901a7519ce02d77a94985b" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", "futures 0.3.28", @@ -5239,11 +5331,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.6.5", @@ -5265,15 +5357,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -5293,20 +5376,20 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ "num-traits", ] [[package]] name = "num-format" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b862ff8df690cf089058c98b183676a7ed0f974cc08b426800093227cbff3b" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "itoa", ] @@ -5334,9 +5417,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", @@ -5344,11 +5427,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.2", "libc", ] @@ -5359,16 +5442,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "crc32fast", - "hashbrown", - "indexmap", + "hashbrown 0.12.3", + "indexmap 1.9.3", "memchr", ] [[package]] name = "object" -version = "0.30.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] @@ -5444,7 +5527,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5609,12 +5692,6 @@ dependencies = [ "xcm-executor", ] -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "p256" version = "0.11.1" @@ -5623,7 +5700,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -5634,7 +5711,7 @@ checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -5915,7 +5992,7 @@ name = "pallet-contracts" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6fa7fe1326ecaab9921c2c3888530ad679cfbb87" dependencies = [ - "bitflags", + "bitflags 1.3.2", "frame-benchmarking", "frame-support", "frame-system", @@ -5944,7 +6021,7 @@ name = "pallet-contracts-primitives" version = "7.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6fa7fe1326ecaab9921c2c3888530ad679cfbb87" dependencies = [ - "bitflags", + "bitflags 1.3.2", "parity-scale-codec", "sp-runtime", "sp-std", @@ -5958,7 +6035,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6f dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -6512,7 +6589,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -6768,11 +6845,11 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.2.2" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "bitvec", "byte-slice-cast", "bytes", @@ -6783,14 +6860,14 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -6807,9 +6884,9 @@ checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -6829,7 +6906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.6", + "parking_lot_core 0.9.8", ] [[package]] @@ -6841,29 +6918,29 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] [[package]] name = "parking_lot_core" -version = "0.9.6" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.42.0", + "windows-targets", ] [[package]] name = "paste" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -6880,7 +6957,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6909,15 +6986,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -6925,9 +7002,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -6935,56 +7012,56 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] name = "pest_meta" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.0.0", ] [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] @@ -6995,9 +7072,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -7017,9 +7094,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" @@ -7218,7 +7295,7 @@ dependencies = [ "fatality", "futures 0.3.28", "futures-timer", - "indexmap", + "indexmap 1.9.3", "lru", "parity-scale-codec", "polkadot-erasure-coding", @@ -8050,7 +8127,7 @@ name = "polkadot-runtime-parachains" version = "0.9.37" source = "git+https://github.com/zeitgeistpm/polkadot.git?branch=v0.9.37-recent-bootnodes#7632261076558a807a6d4585df9cea8e0f2ded05" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bitvec", "derive_more", "frame-benchmarking", @@ -8202,7 +8279,7 @@ dependencies = [ "arrayvec 0.5.2", "fatality", "futures 0.3.28", - "indexmap", + "indexmap 1.9.3", "parity-scale-codec", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -8342,16 +8419,18 @@ dependencies = [ [[package]] name = "polling" -version = "2.5.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", + "bitflags 1.3.2", "cfg-if", + "concurrent-queue", "libc", "log", - "wepoll-ffi", - "windows-sys 0.42.0", + "pin-project-lite 0.2.12", + "windows-sys 0.48.0", ] [[package]] @@ -8411,15 +8490,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -8427,12 +8506,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -8466,9 +8545,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", "toml_edit", @@ -8483,7 +8562,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "version_check", ] @@ -8541,14 +8620,14 @@ checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "prost" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -8556,9 +8635,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", @@ -8571,7 +8650,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 1.0.107", + "syn 1.0.109", "tempfile", "which", ] @@ -8591,24 +8670,23 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "prost-types" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes", "prost", ] @@ -8667,9 +8745,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -8739,7 +8817,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.10", ] [[package]] @@ -8778,9 +8856,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -8788,9 +8866,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -8829,7 +8907,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -8838,8 +8925,8 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", - "redox_syscall", + "getrandom 0.2.10", + "redox_syscall 0.2.16", "thiserror", ] @@ -8858,22 +8945,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.14" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" +checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.14" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" +checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] @@ -8890,13 +8977,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.6", + "regex-syntax 0.7.4", ] [[package]] @@ -8905,23 +8993,31 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "resolv-conf" @@ -9132,9 +9228,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -9163,7 +9259,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.18", ] [[package]] @@ -9177,12 +9273,12 @@ dependencies = [ [[package]] name = "rustix" -version = "0.35.13" +version = "0.35.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +checksum = "6380889b07a03b5ecf1d44dc9ede6fd2145d84b502a2a9ca0b03c48e0cc3220f" dependencies = [ - "bitflags", - "errno", + "bitflags 1.3.2", + "errno 0.2.8", "io-lifetimes 0.7.5", "libc", "linux-raw-sys 0.0.46", @@ -9191,16 +9287,29 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.7" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ - "bitflags", - "errno", - "io-lifetimes 1.0.4", + "bitflags 1.3.2", + "errno 0.3.2", + "io-lifetimes 1.0.11", "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.42.0", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno 0.3.2", + "libc", + "linux-raw-sys 0.4.5", + "windows-sys 0.48.0", ] [[package]] @@ -9230,9 +9339,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -9242,18 +9351,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", ] [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rw-stream-sink" @@ -9268,9 +9377,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safe-mix" @@ -9390,7 +9499,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -9727,7 +9836,7 @@ dependencies = [ "libc", "log", "once_cell", - "rustix 0.35.13", + "rustix 0.35.14", "sc-allocator", "sc-executor-common", "sp-runtime-interface", @@ -9892,7 +10001,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6fa7fe1326ecaab9921c2c3888530ad679cfbb87" dependencies = [ "async-trait", - "bitflags", + "bitflags 1.3.2", "bytes", "futures 0.3.28", "futures-timer", @@ -10315,7 +10424,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -10374,9 +10483,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ "bitvec", "cfg-if", @@ -10388,23 +10497,23 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -10427,15 +10536,15 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "sct" @@ -10477,7 +10586,7 @@ checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct", "der", - "generic-array 0.14.6", + "generic-array 0.14.7", "pkcs8", "subtle", "zeroize", @@ -10512,11 +10621,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -10525,9 +10634,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -10553,9 +10662,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" dependencies = [ "serde", ] @@ -10568,29 +10677,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -10639,7 +10748,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -10669,22 +10778,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -10705,9 +10814,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -10718,7 +10827,7 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -10742,18 +10851,18 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "slice-group-by" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "slot-range-helper" @@ -10778,9 +10887,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "snap" @@ -10790,31 +10899,41 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774d05a3edae07ce6d68ea6984f3c05e9bba8927e3dd591e3b479e5b03213d0d" +checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-rc.3", + "curve25519-dalek 4.0.0", "rand_core 0.6.4", "ring", "rustc_version 0.4.0", - "sha2 0.10.6", + "sha2 0.10.7", "subtle", ] [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.1" @@ -10859,7 +10978,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11052,7 +11171,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6f dependencies = [ "array-bytes 4.2.0", "base58", - "bitflags", + "bitflags 1.3.2", "blake2", "dyn-clonable", "ed25519-zebra", @@ -11094,8 +11213,8 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6f dependencies = [ "blake2", "byteorder", - "digest 0.10.6", - "sha2 0.10.6", + "digest 0.10.7", + "sha2 0.10.7", "sha3", "sp-std", "twox-hash", @@ -11109,7 +11228,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11128,7 +11247,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6f dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11347,7 +11466,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11473,7 +11592,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#6f dependencies = [ "ahash", "hash-db", - "hashbrown", + "hashbrown 0.12.3", "lazy_static", "lru", "memory-db", @@ -11514,7 +11633,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11569,9 +11688,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.38.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" +checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" dependencies = [ "Inflector", "num-format", @@ -11612,7 +11731,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg_aliases", "libc", "parking_lot 0.11.2", @@ -11631,7 +11750,7 @@ dependencies = [ "memchr", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11644,7 +11763,7 @@ dependencies = [ "memchr", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11685,7 +11804,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11731,12 +11850,12 @@ dependencies = [ [[package]] name = "substrate-fixed" version = "0.5.9" -source = "git+https://github.com/encointer/substrate-fixed#a4fb461aae6205ffc55bed51254a40c52be04e5d" +source = "git+https://github.com/encointer/substrate-fixed#df67f97a6db9b40215f105613b381ca82f1e2ff4" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "typenum 1.16.0 (git+https://github.com/encointer/typenum?tag=v1.16.0)", + "typenum 1.16.0 (git+https://github.com/encointer/typenum?tag=polkadot-v1.0.0)", ] [[package]] @@ -11862,9 +11981,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -11873,9 +11992,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -11890,17 +12009,17 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "unicode-xid", ] [[package]] name = "system-configuration" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] @@ -11923,22 +12042,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "tempfile" -version = "3.3.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "fastrand 2.0.0", + "redox_syscall 0.3.5", + "rustix 0.38.8", + "windows-sys 0.48.0", ] [[package]] @@ -11952,9 +12070,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-case" @@ -11975,7 +12093,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11987,7 +12105,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "test-case-core", ] @@ -12007,22 +12125,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -12033,10 +12151,11 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -12075,20 +12194,19 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.4+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" dependencies = [ "cc", - "fs_extra", "libc", ] [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -12135,7 +12253,7 @@ dependencies = [ "pbkdf2 0.11.0", "rand 0.8.5", "rustc-hash", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -12163,39 +12281,38 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.24.2" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot 0.12.1", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] @@ -12211,27 +12328,27 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", "tokio", "tokio-util", ] [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", "tokio", "tracing", ] @@ -12247,19 +12364,19 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.5.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.18.0" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", - "nom8", + "indexmap 2.0.0", "toml_datetime", + "winnow", ] [[package]] @@ -12279,14 +12396,14 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-util", "http", "http-body", "http-range-header", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", "tower-layer", "tower-service", ] @@ -12311,27 +12428,27 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.12", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -12378,7 +12495,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -12390,7 +12507,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -12444,7 +12561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", "log", "rustc-hex", "smallvec", @@ -12477,7 +12594,7 @@ dependencies = [ "lazy_static", "rand 0.8.5", "smallvec", - "socket2", + "socket2 0.4.9", "thiserror", "tinyvec", "tokio", @@ -12574,7 +12691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "digest 0.10.6", + "digest 0.10.7", "rand 0.8.5", "static_assertions", ] @@ -12588,7 +12705,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "typenum" version = "1.16.0" -source = "git+https://github.com/encointer/typenum?tag=v1.16.0#4c8dddaa8bdd13130149e43b4085ad14e960617f" +source = "git+https://github.com/encointer/typenum?tag=polkadot-v1.0.0#4cba9a73f7e94ba38c824616efab93f177c9a556" dependencies = [ "parity-scale-codec", "scale-info", @@ -12596,15 +12713,15 @@ dependencies = [ [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", @@ -12614,15 +12731,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -12651,7 +12768,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -12685,22 +12802,28 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna 0.4.0", "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.10", ] [[package]] @@ -12744,22 +12867,20 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -12783,9 +12904,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -12793,24 +12914,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -12820,9 +12941,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12830,22 +12951,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-instrument" @@ -12989,7 +13110,7 @@ version = "0.89.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" dependencies = [ - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -13010,7 +13131,7 @@ dependencies = [ "anyhow", "bincode", "cfg-if", - "indexmap", + "indexmap 1.9.3", "libc", "log", "object 0.29.0", @@ -13050,7 +13171,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.35.13", + "rustix 0.35.14", "serde", "sha2 0.9.9", "toml", @@ -13088,7 +13209,7 @@ dependencies = [ "anyhow", "cranelift-entity", "gimli 0.26.2", - "indexmap", + "indexmap 1.9.3", "log", "object 0.29.0", "serde", @@ -13113,7 +13234,7 @@ dependencies = [ "log", "object 0.29.0", "rustc-demangle", - "rustix 0.35.13", + "rustix 0.35.14", "serde", "target-lexicon", "thiserror", @@ -13131,7 +13252,7 @@ checksum = "f671b588486f5ccec8c5a3dba6b4c07eac2e66ab8c60e6f4e53717c77f709731" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.35.13", + "rustix 0.35.14", ] [[package]] @@ -13143,7 +13264,7 @@ dependencies = [ "anyhow", "cc", "cfg-if", - "indexmap", + "indexmap 1.9.3", "libc", "log", "mach", @@ -13151,7 +13272,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.35.13", + "rustix 0.35.14", "thiserror", "wasmtime-asm-macros", "wasmtime-environ", @@ -13173,9 +13294,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -13233,7 +13354,7 @@ dependencies = [ "sdp", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "stun", "thiserror", "time 0.3.25", @@ -13295,14 +13416,14 @@ dependencies = [ "sec1", "serde", "sha1", - "sha2 0.10.6", + "sha2 0.10.7", "signature", "subtle", "thiserror", "tokio", "webpki 0.21.4", "webrtc-util", - "x25519-dalek 2.0.0-rc.3", + "x25519-dalek 2.0.0", "x509-parser 0.13.2", ] @@ -13337,7 +13458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" dependencies = [ "log", - "socket2", + "socket2 0.4.9", "thiserror", "tokio", "webrtc-util", @@ -13404,7 +13525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" dependencies = [ "async-trait", - "bitflags", + "bitflags 1.3.2", "bytes", "cc", "ipnet", @@ -13418,15 +13539,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - [[package]] name = "westend-runtime" version = "0.9.37" @@ -13544,9 +13656,9 @@ dependencies = [ [[package]] name = "widestring" -version = "0.5.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "winapi" @@ -13592,6 +13704,15 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.36.1" @@ -13611,20 +13732,50 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -13640,9 +13791,15 @@ checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -13658,9 +13815,15 @@ checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -13676,9 +13839,15 @@ checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -13694,15 +13863,27 @@ checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -13718,17 +13899,33 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +dependencies = [ + "memchr", +] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -13753,11 +13950,11 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.0-rc.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek 4.0.0-rc.3", + "curve25519-dalek 4.0.0", "rand_core 0.6.4", "serde", "zeroize", @@ -13886,7 +14083,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -14132,14 +14329,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", - "synstructure", + "syn 2.0.29", ] [[package]] @@ -14163,7 +14359,7 @@ dependencies = [ name = "zrml-court" version = "0.3.11" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "frame-benchmarking", "frame-support", "frame-system", @@ -14334,7 +14530,7 @@ dependencies = [ "cfg-if", "frame-support", "frame-system", - "hashbrown", + "hashbrown 0.12.3", "pallet-balances", "pallet-timestamp", "parity-scale-codec", @@ -14482,9 +14678,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.5+zstd.1.5.2" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index ff9dad44a..960f47c8e 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -2298,39 +2298,50 @@ mod pallet { Error::::MarketDoesNotHaveCourtMechanism ); - let court_id = >::get(market_id) - .ok_or(Error::::MarketIdToCourtIdNotFound)?; - - let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; + // oracle outcome is added by pm-pallet + let mut gd_outcomes: Vec> = + Vec::new(); - let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; - let oracle_outcome = &report.outcome; + let mut appeals_len = 0u32; + let mut draws_len = 0u32; - let appeals_len = court.appeals.len() as u32; - - let gd_outcomes = court - .appeals - .iter() - .filter_map(|a| { - match a.appealed_vote_item.clone().into_outcome() { - // oracle outcome is added by pm pallet - Some(outcome) if outcome != *oracle_outcome => Some(GlobalDisputeItem { - outcome, - // we have no better global dispute outcome owner - owner: Self::treasury_account_id(), - // initial vote amount - initial_vote_amount: >::zero(), - }), - _ => None, - } - }) - .collect::>>(); + // None case can happen if no dispute could be created, + // because there is not enough juror and delegator stake, + // in this case allow a global dispute + if let Some(court_id) = >::get(market_id) { + let court = >::get(court_id).ok_or(Error::::CourtNotFound)?; + + appeals_len = court.appeals.len() as u32; + + let report = market.report.as_ref().ok_or(Error::::MarketReportNotFound)?; + let oracle_outcome = &report.outcome; + + gd_outcomes = court + .appeals + .iter() + .filter_map(|a| { + match a.appealed_vote_item.clone().into_outcome() { + // oracle outcome is added by pm pallet + Some(outcome) if outcome != *oracle_outcome => { + Some(GlobalDisputeItem { + outcome, + // we have no better global dispute outcome owner + owner: Self::treasury_account_id(), + // initial vote amount + initial_vote_amount: >::zero(), + }) + } + _ => None, + } + }) + .collect::>>(); - let old_draws = SelectedDraws::::get(court_id); - let draws_len = old_draws.len() as u32; - Self::unlock_participants_from_last_draw(court_id, old_draws); - >::remove(court_id); - >::remove(court_id); + let old_draws = SelectedDraws::::get(court_id); + draws_len = old_draws.len() as u32; + Self::unlock_participants_from_last_draw(court_id, old_draws); + >::remove(court_id); + >::remove(court_id); + } let res = ResultWithWeightInfo { result: gd_outcomes, diff --git a/zrml/global-disputes/src/lib.rs b/zrml/global-disputes/src/lib.rs index 24511617d..e2ba21e7e 100644 --- a/zrml/global-disputes/src/lib.rs +++ b/zrml/global-disputes/src/lib.rs @@ -38,7 +38,6 @@ pub type InitialItemOf = crate::types::InitialItem, BalanceOf< #[frame_support::pallet] mod pallet { use crate::{types::*, weights::WeightInfoZeitgeist, GlobalDisputesPalletApi, InitialItemOf}; - use alloc::collections::BTreeSet; use core::marker::PhantomData; use frame_support::{ ensure, log, @@ -221,8 +220,6 @@ mod pallet { pub enum Error { /// Sender tried to vote with an amount below a defined minimum. AmountTooLow, - /// To start a global dispute, at least two outcomes are required. - AtLeastTwoUniqueOutcomesRequired, /// The global dispute status is invalid for this operation. InvalidGlobalDisputeStatus, /// Sender does not have enough funds for the vote on an outcome. @@ -332,11 +329,12 @@ mod pallet { /// /// Complexity: `O(n)`, /// where `n` is the number of all existing outcomes for a global dispute. - #[frame_support::transactional] + #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::refund_vote_fees( T::RemoveKeysLimit::get(), T::MaxOwners::get(), ))] + #[frame_support::transactional] pub fn refund_vote_fees( origin: OriginFor, #[pallet::compact] market_id: MarketIdOf, @@ -815,19 +813,6 @@ mod pallet { Error::::GlobalDisputeAlreadyExists ); - let mut outcome_set = BTreeSet::new(); - // insert returns true if the outcome is already present - let outcome_count = initial_items - .iter() - // insert returns true if the outcome was not already present (unqiue) - .filter(|item| outcome_set.insert(item.outcome.clone())) - .take(2) // Limit the iterator to at most two unique outcomes - .count(); - - if outcome_count < 2 { - return Err(Error::::AtLeastTwoUniqueOutcomesRequired.into()); - } - for InitialItem { outcome, owner, amount } in initial_items { ensure!(market.matches_outcome_report(outcome), Error::::OutcomeMismatch); diff --git a/zrml/global-disputes/src/mock.rs b/zrml/global-disputes/src/mock.rs index bdd28b3f1..3f73f1ee2 100644 --- a/zrml/global-disputes/src/mock.rs +++ b/zrml/global-disputes/src/mock.rs @@ -23,7 +23,6 @@ use frame_support::{ pallet_prelude::{DispatchError, Weight}, parameter_types, traits::Everything, - BoundedVec, }; use sp_runtime::{ testing::Header, @@ -37,8 +36,8 @@ use zeitgeist_primitives::{ }, traits::DisputeResolutionApi, types::{ - AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketDispute, - MarketId, Moment, UncheckedExtrinsicTest, + AccountIdTest, Asset, Balance, BlockNumber, BlockTest, Hash, Index, Market, MarketId, + Moment, UncheckedExtrinsicTest, }, }; @@ -72,7 +71,6 @@ impl DisputeResolutionApi for NoopResolution { type Balance = Balance; type BlockNumber = BlockNumber; type MarketId = MarketId; - type MaxDisputes = u32; type Moment = Moment; fn resolve( @@ -102,12 +100,6 @@ impl DisputeResolutionApi for NoopResolution { fn remove_auto_resolve(_market_id: &Self::MarketId, _resolve_at: Self::BlockNumber) -> u32 { 0u32 } - - fn get_disputes( - _market_id: &Self::MarketId, - ) -> BoundedVec, Self::MaxDisputes> { - Default::default() - } } parameter_types! { diff --git a/zrml/global-disputes/src/tests.rs b/zrml/global-disputes/src/tests.rs index 55d6485e4..bd861221b 100644 --- a/zrml/global-disputes/src/tests.rs +++ b/zrml/global-disputes/src/tests.rs @@ -220,45 +220,6 @@ fn start_global_dispute_fails_if_outcome_mismatch() { }); } -#[test] -fn start_global_dispute_fails_if_less_than_two_outcomes() { - ExtBuilder::default().build().execute_with(|| { - let market_id = 0u128; - let market = market_mock::(); - Markets::::insert(market_id, market); - - let initial_items = vec![InitialItem { - outcome: OutcomeReport::Scalar(0), - owner: ALICE, - amount: SETUP_AMOUNT, - }]; - assert_eq!( - GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), - Err(Error::::AtLeastTwoUniqueOutcomesRequired.into()) - ); - }); -} - -#[test] -fn start_global_dispute_fails_if_not_two_unique_outcomes() { - ExtBuilder::default().build().execute_with(|| { - let market_id = 0u128; - let market = market_mock::(); - Markets::::insert(market_id, market); - - // three times the same outcome => not at least two unique outcomes - let initial_items = vec![ - InitialItem { outcome: OutcomeReport::Scalar(0), owner: ALICE, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(0), owner: BOB, amount: SETUP_AMOUNT }, - InitialItem { outcome: OutcomeReport::Scalar(0), owner: CHARLIE, amount: SETUP_AMOUNT }, - ]; - assert_eq!( - GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice()), - Err(Error::::AtLeastTwoUniqueOutcomesRequired.into()) - ); - }); -} - #[test] fn start_global_dispute_fails_if_already_exists() { ExtBuilder::default().build().execute_with(|| { diff --git a/zrml/prediction-markets/src/benchmarks.rs b/zrml/prediction-markets/src/benchmarks.rs index 8c4b54ffe..1aa167f22 100644 --- a/zrml/prediction-markets/src/benchmarks.rs +++ b/zrml/prediction-markets/src/benchmarks.rs @@ -45,7 +45,6 @@ use zeitgeist_primitives::{ }, }; use zrml_authorized::Pallet as AuthorizedPallet; -#[cfg(feature = "with-global-disputes")] use zrml_global_disputes::GlobalDisputesPalletApi; use zrml_market_commons::MarketCommonsPalletApi; diff --git a/zrml/prediction-markets/src/lib.rs b/zrml/prediction-markets/src/lib.rs index 6449be6e6..2c92c2ff4 100644 --- a/zrml/prediction-markets/src/lib.rs +++ b/zrml/prediction-markets/src/lib.rs @@ -97,7 +97,6 @@ mod pallet { pub type CacheSize = ConstU32<64>; pub type EditReason = BoundedVec::MaxEditReasonLen>; pub type RejectReason = BoundedVec::MaxRejectReasonLen>; - #[cfg(feature = "with-global-disputes")] type InitialItemOf = InitialItem<::AccountId, BalanceOf>; macro_rules! impl_unreserve_bond { @@ -344,7 +343,6 @@ mod pallet { MarketIdsForEdit::::remove(market_id); } - #[cfg(feature = "with-global-disputes")] if T::GlobalDisputes::is_active(&market_id) { T::GlobalDisputes::destroy_global_dispute(&market_id)?; } @@ -1453,7 +1451,10 @@ mod pallet { ensure_signed(origin)?; let market = >::market(&market_id)?; - ensure!(market.status == MarketStatus::Disputed, Error::::InvalidMarketStatus); + ensure!( + matches!(market.status, MarketStatus::Disputed | MarketStatus::Reported), + Error::::InvalidMarketStatus + ); ensure!( matches!(market.dispute_mechanism, MarketDisputeMechanism::Court), @@ -1514,6 +1515,15 @@ mod pallet { // ignore first of tuple because we always have max disputes let (_, ids_len_2) = Self::clear_auto_resolve(&market_id)?; + if market.status == MarketStatus::Reported { + // this is the case that a dispute can not be initiated, + // because court has not enough juror and delegator stake (dispute errors) + >::mutate_market(&market_id, |m| { + m.status = MarketStatus::Disputed; + Ok(()) + })?; + } + // global disputes uses DisputeResolution API to control its resolution let ids_len_1 = T::GlobalDisputes::start_global_dispute(&market_id, initial_items.as_slice())?; diff --git a/zrml/prediction-markets/src/tests.rs b/zrml/prediction-markets/src/tests.rs index 895d3cdb6..2269a8455 100644 --- a/zrml/prediction-markets/src/tests.rs +++ b/zrml/prediction-markets/src/tests.rs @@ -50,7 +50,10 @@ use zeitgeist_primitives::{ Moment, MultiHash, OutcomeReport, PoolStatus, ScalarPosition, ScoringRule, }, }; -use zrml_global_disputes::GlobalDisputesPalletApi; +use zrml_global_disputes::{ + types::{OutcomeInfo, Possession}, + GlobalDisputesPalletApi, Outcomes, PossessionOf, +}; use zrml_market_commons::MarketCommonsPalletApi; use zrml_swaps::Pools; const LIQUIDITY: u128 = 100 * BASE; @@ -3447,7 +3450,6 @@ fn it_appeals_a_court_market_to_global_dispute() { OutcomeReport::Categorical(0) )); - let dispute_block = report_at; assert_ok!(PredictionMarkets::dispute(RuntimeOrigin::signed(CHARLIE), market_id,)); for _ in 0..(MaxAppeals::get() - 1) { @@ -3472,87 +3474,41 @@ fn it_appeals_a_court_market_to_global_dispute() { CError::::MaxAppealsReached ); - assert!(!GlobalDisputes::is_started(&market_id)); + assert!(!GlobalDisputes::does_exist(&market_id)); assert_ok!(PredictionMarkets::start_global_dispute(RuntimeOrigin::signed(BOB), market_id)); let now = >::block_number(); - assert!(GlobalDisputes::is_started(&market_id)); + assert!(GlobalDisputes::does_exist(&market_id)); System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); - #[cfg(feature = "with-global-disputes")] - { - use zrml_global_disputes::{ - types::{OutcomeInfo, Possession}, - GlobalDisputesPalletApi, Outcomes, PossessionOf, - }; - - let now = >::block_number(); - assert_ok!(PredictionMarkets::start_global_dispute( - RuntimeOrigin::signed(CHARLIE), - market_id - )); - - // report check - let possession: PossessionOf = Possession::Shared { - owners: frame_support::BoundedVec::try_from(vec![BOB]).unwrap(), - }; - let outcome_info = OutcomeInfo { outcome_sum: Zero::zero(), possession }; - assert_eq!( - Outcomes::::get(market_id, &OutcomeReport::Categorical(0)).unwrap(), - outcome_info - ); - let possession: PossessionOf = Possession::Shared { - owners: frame_support::BoundedVec::try_from(vec![CHARLIE]).unwrap(), - }; - for i in 1..=::MaxDisputes::get() { - let dispute_bond = crate::default_dispute_bond::((i - 1).into()); - let outcome_info = - OutcomeInfo { outcome_sum: dispute_bond, possession: possession.clone() }; - assert_eq!( - Outcomes::::get( - market_id, - &OutcomeReport::Categorical(i.saturated_into()) - ) - .unwrap(), - outcome_info - ); - } - - // remove_last_dispute_from_market_ids_per_dispute_block works - let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); - assert_eq!(removable_market_ids.len(), 0); - - let add_outcome_end = now + GlobalDisputes::get_add_outcome_period(); - let vote_end = add_outcome_end + GlobalDisputes::get_vote_period(); - let market_ids = MarketIdsPerDisputeBlock::::get(vote_end); - assert_eq!(market_ids, vec![market_id]); - assert!(GlobalDisputes::is_active(&market_id)); - System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); - - assert_noop!( - PredictionMarkets::start_global_dispute(RuntimeOrigin::signed(CHARLIE), market_id), - Error::::GlobalDisputeExistsAlready - ); - } - - // remove_last_dispute_from_market_ids_per_dispute_block works - let removable_market_ids = MarketIdsPerDisputeBlock::::get(dispute_block); - assert_eq!(removable_market_ids.len(), 0); - - let market_ids = MarketIdsPerDisputeBlock::::get( - now + ::GlobalDisputePeriod::get(), + // report check + let possession: PossessionOf = + Possession::Shared { owners: frame_support::BoundedVec::try_from(vec![BOB]).unwrap() }; + let outcome_info = OutcomeInfo { outcome_sum: Zero::zero(), possession }; + assert_eq!( + Outcomes::::get(market_id, &OutcomeReport::Categorical(0)).unwrap(), + outcome_info ); + + let add_outcome_end = now + GlobalDisputes::get_add_outcome_period(); + let vote_end = add_outcome_end + GlobalDisputes::get_vote_period(); + let market_ids = MarketIdsPerDisputeBlock::::get(vote_end); assert_eq!(market_ids, vec![market_id]); - assert!(GlobalDisputes::is_started(&market_id)); - System::assert_last_event(Event::GlobalDisputeStarted(market_id).into()); + assert!(GlobalDisputes::is_active(&market_id)); assert_noop!( PredictionMarkets::start_global_dispute(RuntimeOrigin::signed(CHARLIE), market_id), - Error::::GlobalDisputeAlreadyStarted + Error::::GlobalDisputeExistsAlready ); - } + }; + ExtBuilder::default().build().execute_with(|| { + test(Asset::Ztg); + }); + #[cfg(feature = "parachain")] + ExtBuilder::default().build().execute_with(|| { + test(Asset::ForeignAsset(100)); }); } From 685b5d32f7ed5e83186e7c84d3720a4e1d124fa3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 22 Aug 2023 17:21:12 +0200 Subject: [PATCH 357/368] update changelog for devs --- docs/changelog_for_devs.md | 46 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index fb4678d1f..112708314 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -20,29 +20,16 @@ APIs/RPC interface. All things about Global Disputes Fix ⚠️ : -- Added new dispatchable function: - - `refund_vote_fees` - Return all vote funds and fees, when a global dispute - was destroyed. -- Added the following events: - - `OutcomeOwnerRewarded` for `Possession::Paid` - - `OutcomeOwnersRewarded` for `Possession::Shared` - - `OutcomesFullyCleaned` and `OutcomesPartiallyCleaned` for extrinsic - `refund_vote_fees` -- Removed the following events: -- Added enum `Possession` with variants: - - `Paid { owner: AccountId, fee: Balance }` - - `Shared { owners: BoundedVec }` -- `OutcomeInfo` has the following fields: - - `outcome_sum: Balance` - - `possession: Possession` - Replace `WinnerInfo` by `GlobalDisputeInfo` with the following fields: - `winner_outcome: OutcomeReport` - `outcome_info: OutcomeInfo` - `status: GdStatus` -- Added `GdStatus` with the following enum variants: - - `Active { add_outcome_end: BlockNumber, vote_end: BlockNumber }` - - `Finished` - - `Destroyed` + +### Removed + +All things about Global Disputes Fix ⚠️ : +- Removed the following event: + - `OutcomeOwnersRewardedWithNoFunds` ### Added @@ -81,6 +68,27 @@ All things about Global Disputes Fix ⚠️ : vote with the plurality, were denounced or did not reveal their vote. - `InflationSet` - The yearly inflation rate of the court system was set. +All things about Global Disputes Fix ⚠️ : + +- Added new dispatchable function: + - `refund_vote_fees` - Return all vote funds and fees, when a global dispute + was destroyed. +- Added the following events: + - `OutcomeOwnerRewarded` for `Possession::Paid` + - `OutcomeOwnersRewarded` for `Possession::Shared` + - `OutcomesFullyCleaned` and `OutcomesPartiallyCleaned` for extrinsic + `refund_vote_fees` +- Added enum `Possession` with variants: +- `Paid { owner: AccountId, fee: Balance }` +- `Shared { owners: BoundedVec }` +- `OutcomeInfo` has the following fields: + - `outcome_sum: Balance` + - `possession: Possession` +- Added `GdStatus` with the following enum variants: + - `Active { add_outcome_end: BlockNumber, vote_end: BlockNumber }` + - `Finished` + - `Destroyed` + ## v0.3.11 [#1049]: https://github.com/zeitgeistpm/zeitgeist/pull/1049 From 818b492f410d3e9823effb6ccfae3d20d9669ff9 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 22 Aug 2023 17:22:49 +0200 Subject: [PATCH 358/368] use imperative --- docs/changelog_for_devs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index 112708314..08a757525 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -28,7 +28,7 @@ All things about Global Disputes Fix ⚠️ : ### Removed All things about Global Disputes Fix ⚠️ : -- Removed the following event: +- Remove the following event: - `OutcomeOwnersRewardedWithNoFunds` ### Added @@ -70,21 +70,21 @@ All things about Global Disputes Fix ⚠️ : All things about Global Disputes Fix ⚠️ : -- Added new dispatchable function: +- Add new dispatchable function: - `refund_vote_fees` - Return all vote funds and fees, when a global dispute was destroyed. -- Added the following events: +- Add the following events: - `OutcomeOwnerRewarded` for `Possession::Paid` - `OutcomeOwnersRewarded` for `Possession::Shared` - `OutcomesFullyCleaned` and `OutcomesPartiallyCleaned` for extrinsic `refund_vote_fees` -- Added enum `Possession` with variants: +- Add enum `Possession` with variants: - `Paid { owner: AccountId, fee: Balance }` - `Shared { owners: BoundedVec }` - `OutcomeInfo` has the following fields: - `outcome_sum: Balance` - `possession: Possession` -- Added `GdStatus` with the following enum variants: +- Add `GdStatus` with the following enum variants: - `Active { add_outcome_end: BlockNumber, vote_end: BlockNumber }` - `Finished` - `Destroyed` From 84ec2a06662cc3cbc6ea55efa7d12d286919d810 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 22 Aug 2023 17:23:35 +0200 Subject: [PATCH 359/368] fmt --- docs/changelog_for_devs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog_for_devs.md b/docs/changelog_for_devs.md index 08a757525..98d072c2a 100644 --- a/docs/changelog_for_devs.md +++ b/docs/changelog_for_devs.md @@ -28,6 +28,7 @@ All things about Global Disputes Fix ⚠️ : ### Removed All things about Global Disputes Fix ⚠️ : + - Remove the following event: - `OutcomeOwnersRewardedWithNoFunds` From b14060aea8c36793613c4a86b2064517fe07a28e Mon Sep 17 00:00:00 2001 From: Chralt Date: Tue, 22 Aug 2023 19:58:10 +0200 Subject: [PATCH 360/368] Update zrml/court/src/lib.rs Co-authored-by: Harald Heckmann --- zrml/court/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index ff9dad44a..a728493dd 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1262,7 +1262,7 @@ mod pallet { .saturated_into::() .saturated_into::>(); if inflation_period_mint >= log_threshold { - log::error!( + log::warn!( "Inflation per period is greater than the threshold. Inflation per period: \ {:?}, threshold: {:?}", inflation_period_mint, From a5ca6a45587d9a4ea662ecd931a47132866567f5 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 22 Aug 2023 20:03:44 +0200 Subject: [PATCH 361/368] reduce court InflationPeriod for Battery Station --- runtime/battery-station/src/parameters.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/battery-station/src/parameters.rs b/runtime/battery-station/src/parameters.rs index 3afd12fd9..2c6973b0f 100644 --- a/runtime/battery-station/src/parameters.rs +++ b/runtime/battery-station/src/parameters.rs @@ -113,7 +113,7 @@ parameter_types! { /// The court lock identifier. pub const CourtLockId: LockIdentifier = COURT_LOCK_ID; /// The time in which the inflation is periodically issued. - pub const InflationPeriod: BlockNumber = 30 * BLOCKS_PER_DAY; + pub const InflationPeriod: BlockNumber = 3 * BLOCKS_PER_DAY; /// The maximum number of appeals until the court fails. pub const MaxAppeals: u32 = 4; /// The maximum number of delegations per juror account. From 507592395c4ee419416eda83fc0fda15bcf63303 Mon Sep 17 00:00:00 2001 From: Chralt Date: Tue, 22 Aug 2023 20:04:13 +0200 Subject: [PATCH 362/368] Update zrml/court/src/lib.rs Co-authored-by: Harald Heckmann --- zrml/court/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index a728493dd..c91315cf9 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1308,6 +1308,19 @@ mod pallet { } } + let remainder = total_mint.peek(); + if total_mint.drop_zero().is_err() { + log::warn!( + "Total issued tokens were not completely distributed, total: \ + {:?}, leftover: {:?}", + inflation_period_mint, + remainder + ); + + T::Currency::burn(remainder); + debug_assert!(false); + } + T::WeightInfo::handle_inflation(pool_len) } From 6ec8c5d1052a0bd38fe0d8dc12f7159eedbb3bc3 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Tue, 22 Aug 2023 20:08:04 +0200 Subject: [PATCH 363/368] fmt --- zrml/court/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index c91315cf9..727d87232 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1311,12 +1311,12 @@ mod pallet { let remainder = total_mint.peek(); if total_mint.drop_zero().is_err() { log::warn!( - "Total issued tokens were not completely distributed, total: \ - {:?}, leftover: {:?}", + "Total issued tokens were not completely distributed, total: {:?}, leftover: \ + {:?}", inflation_period_mint, remainder ); - + T::Currency::burn(remainder); debug_assert!(false); } From 0d7a506d4f05eedd8f9ec979118adec3bb5caaa7 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 23 Aug 2023 10:32:47 +0200 Subject: [PATCH 364/368] fix tests --- zrml/court/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 727d87232..7fd2375ea 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1274,7 +1274,7 @@ mod pallet { // safe guard: inflation per period should never exceed the yearly inflation amount if inflation_period_mint > yearly_inflation_amount { debug_assert!(false); - return Weight::zero(); + return T::WeightInfo::handle_inflation(0u32); } let mut total_mint = T::Currency::issue(inflation_period_mint); @@ -1289,6 +1289,9 @@ mod pallet { .fold(0u128, |acc, pool_item| { acc.saturating_add(pool_item.stake.saturated_into::()) }); + if total_stake.is_zero() { + return T::WeightInfo::handle_inflation(0u32); + } for CourtPoolItem { stake, court_participant, joined_at, .. } in pool { if !at_least_one_inflation_period(joined_at) { // participants who joined and didn't wait @@ -1310,7 +1313,7 @@ mod pallet { let remainder = total_mint.peek(); if total_mint.drop_zero().is_err() { - log::warn!( + log::debug!( "Total issued tokens were not completely distributed, total: {:?}, leftover: \ {:?}", inflation_period_mint, @@ -1318,7 +1321,6 @@ mod pallet { ); T::Currency::burn(remainder); - debug_assert!(false); } T::WeightInfo::handle_inflation(pool_len) From 459e9ff808ff14c248128c349e5fa17a5b26dbf4 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 23 Aug 2023 10:35:41 +0200 Subject: [PATCH 365/368] fix issue tokens without burn --- zrml/court/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 7fd2375ea..62c6dcd66 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1277,8 +1277,6 @@ mod pallet { return T::WeightInfo::handle_inflation(0u32); } - let mut total_mint = T::Currency::issue(inflation_period_mint); - let pool = >::get(); let pool_len = pool.len() as u32; let at_least_one_inflation_period = @@ -1292,6 +1290,9 @@ mod pallet { if total_stake.is_zero() { return T::WeightInfo::handle_inflation(0u32); } + + let mut total_mint = T::Currency::issue(inflation_period_mint); + for CourtPoolItem { stake, court_participant, joined_at, .. } in pool { if !at_least_one_inflation_period(joined_at) { // participants who joined and didn't wait From 3a33893696e1e0926190c6f91c9057698baec817 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 23 Aug 2023 10:48:23 +0200 Subject: [PATCH 366/368] remove unnecessary error return --- zrml/court/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index 62c6dcd66..bc9bbe085 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -1292,7 +1292,7 @@ mod pallet { } let mut total_mint = T::Currency::issue(inflation_period_mint); - + for CourtPoolItem { stake, court_participant, joined_at, .. } in pool { if !at_least_one_inflation_period(joined_at) { // participants who joined and didn't wait @@ -1332,10 +1332,7 @@ mod pallet { // Uses Partial Fisher Yates shuffle and drawing without replacement. // The time complexity is O(n). // Return a vector of n unique random numbers between ´MinJurorStake´ and ´max´ (inclusive). - pub(crate) fn get_n_random_section_ends( - n: usize, - max: u128, - ) -> Result, DispatchError> { + pub(crate) fn get_n_random_section_ends(n: usize, max: u128) -> BTreeSet { let mut rng = Self::rng(); let min_juror_stake = T::MinJurorStake::get().saturated_into::(); @@ -1367,7 +1364,7 @@ mod pallet { debug_assert!(random_section_ends.len() == n); - Ok(random_section_ends) + random_section_ends } // Adds active lock amount. @@ -1672,7 +1669,7 @@ mod pallet { Error::::NotEnoughJurorsAndDelegatorsStake ); let random_section_ends = - Self::get_n_random_section_ends(draw_weight, total_unconsumed)?; + Self::get_n_random_section_ends(draw_weight, total_unconsumed); let selections = Self::get_selections(&mut pool, random_section_ends, cumulative_section_ends); >::put(pool); From 30dc38f800586a985e86fc1076379380d162767f Mon Sep 17 00:00:00 2001 From: Chralt Date: Wed, 23 Aug 2023 11:23:59 +0200 Subject: [PATCH 367/368] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c0f1ef05d..537b0b49c 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ check-dummy: RUST_LOG=runtime=trace,try-runtime::cli=trace,executor=trace \ cargo run \ --bin=zeitgeist \ - --features=parachain,try-runtime${ADDITIONAL_FEATURES} \ + --features=parachain,try-runtime \ try-runtime \ --chain=${TRYRUNTIME_CHAIN} \ --runtime=${RUNTIME_PATH} \ From a04810badb7ced960c4b5d96cca007f4e07a8bcd Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Wed, 23 Aug 2023 12:16:12 +0200 Subject: [PATCH 368/368] allow equal amount to previous stake --- zrml/court/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/zrml/court/src/lib.rs b/zrml/court/src/lib.rs index bc9bbe085..a9fbfcfdc 100644 --- a/zrml/court/src/lib.rs +++ b/zrml/court/src/lib.rs @@ -401,7 +401,15 @@ mod pallet { MaxCourtIdReached, /// The caller has not enough funds to join the court with the specified amount. AmountExceedsBalance, - /// After the first join of the court the amount has to be higher than the current stake. + /// After the first join of the court the amount has to be equal or higher than the current stake. + /// This is to ensure the slashable amount in active court rounds + /// is still smaller or equal to the stake. + /// It is also necessary to calculate the `unconsumed` stake properly. + /// Otherwise a juror could just reduce the probability to get selected whenever they want. + /// But this has to be done by `prepare_exit_court` and `exit_court`. + /// Additionally, the `join_court` and `delegate` extrinsics + /// use `extend_lock` and not `set_lock` or `remove_lock`. + /// This means those extrinsics are not meant to get out, but only to get into the court. AmountBelowLastJoin, /// The amount is too low to kick the lowest juror out of the stake-weighted pool. AmountBelowLowestJuror, @@ -1161,7 +1169,7 @@ mod pallet { let mut joined_at = now; if let Some(prev_p_info) = >::get(who) { - ensure!(amount > prev_p_info.stake, Error::::AmountBelowLastJoin); + ensure!(amount >= prev_p_info.stake, Error::::AmountBelowLastJoin); if let Some((index, pool_item)) = Self::get_pool_item(&pool, prev_p_info.stake, who) {