Skip to content

Commit

Permalink
🐛 Start failed evaluation settlement (#331)
Browse files Browse the repository at this point in the history
## What?
- Schedule the start of settlement for a failed evaluation round
- Settlement functions now check that the settlement was started, and the round is correct (with new more descriptive errors)

## Why?
- We forgor 💀

## Testing?
Refactored some tests to adapt to the new logic

## Anything Else?
We need to re-benchmark end_evaluation_failure since it has a new parameter. For now the parameter is added but not used
  • Loading branch information
JuaniRios committed Jun 21, 2024
1 parent 205b7c4 commit 86996b7
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 65 deletions.
7 changes: 6 additions & 1 deletion pallets/funding/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1867,7 +1867,10 @@ mod benchmarks {
}

#[benchmark]
fn end_evaluation_failure() {
fn end_evaluation_failure(
// Insertion attempts in add_to_update_store. Total amount of storage items iterated through in `ProjectsToUpdate`. Leave one free to make the fn succeed
x: Linear<1, { <T as Config>::MaxProjectsToUpdateInsertionAttempts::get() - 1 }>,
) {
// * setup *
let mut inst = BenchInstantiator::<T>::new(None);
<T as Config>::SetPrices::set_prices();
Expand Down Expand Up @@ -1911,6 +1914,8 @@ mod benchmarks {
// move block manually without calling any hooks, to avoid triggering the transition outside the benchmarking context
frame_system::Pallet::<T>::set_block_number(evaluation_end_block + One::one());

fill_projects_to_update::<T>(x, evaluation_end_block + 2u32.into());

// Instead of advancing in time for the automatic `do_evaluation_end` call in on_initialize, we call it directly to benchmark it
#[block]
{
Expand Down
9 changes: 8 additions & 1 deletion pallets/funding/src/functions/2_evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,18 @@ impl<T: Config> Pallet<T> {
// Unsuccessful path
} else {
// * Update storage *

project_details.status = ProjectStatus::FundingFailed;
ProjectsDetails::<T>::insert(project_id, project_details.clone());
let issuer_did = project_details.issuer_did.clone();
DidWithActiveProjects::<T>::set(issuer_did, None);

let insertion_attempts =
match Self::add_to_update_store(now + One::one(), (&project_id, UpdateType::StartSettlement)) {
Ok(insertions) => insertions,
Err(_insertions) => return Err(Error::<T>::TooManyInsertionAttempts.into()),
};

// * Emit events *
Self::deposit_event(
Event::ProjectPhaseTransition {
Expand All @@ -162,7 +169,7 @@ impl<T: Config> Pallet<T> {
.into(),
);
return Ok(PostDispatchInfo {
actual_weight: Some(WeightInfoOf::<T>::end_evaluation_failure()),
actual_weight: Some(WeightInfoOf::<T>::end_evaluation_failure(insertion_attempts)),
pays_fee: Pays::Yes,
});
}
Expand Down
18 changes: 12 additions & 6 deletions pallets/funding/src/functions/6_settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_successful_evaluation(evaluation: EvaluationInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::WrongSettlementOutcome);

// Based on the results of the funding round, the evaluator is either:
// 1. Slashed
Expand Down Expand Up @@ -136,7 +137,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_failed_evaluation(evaluation: EvaluationInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::WrongSettlementOutcome);

let bond = if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) {
Self::slash_evaluator(project_id, &evaluation)?
Expand Down Expand Up @@ -169,7 +171,8 @@ impl<T: Config> Pallet<T> {
let project_metadata = ProjectsMetadata::<T>::get(project_id).ok_or(Error::<T>::ProjectMetadataNotFound)?;
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;

ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::WrongSettlementOutcome);
ensure!(
matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)),
Error::<T>::ImpossibleState
Expand Down Expand Up @@ -231,7 +234,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_failed_bid(bid: BidInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::WrongSettlementOutcome);

let bidder = bid.bidder;

Expand All @@ -258,7 +262,8 @@ impl<T: Config> Pallet<T> {
// Ensure that:
// 1. The project is in the FundingSuccessful state
// 2. The contribution token exists
ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::WrongSettlementOutcome);
ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::<T>::TooEarlyForRound);

let contributor = contribution.contributor;
Expand Down Expand Up @@ -316,7 +321,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_failed_contribution(contribution: ContributionInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::WrongSettlementOutcome);

// Check if the bidder has a future deposit held
let contributor = contribution.contributor;
Expand Down
4 changes: 4 additions & 0 deletions pallets/funding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,10 @@ pub mod pallet {
WrongParaId,
/// Migration channel is not ready for migrations.
ChannelNotReady,
/// Settlement for this project has not yet started.
SettlementNotStarted,
/// Wanted to settle as successful when it failed, or vice versa.
WrongSettlementOutcome,
/// User still has participations that need to be settled before migration.
ParticipationsNotSettled,
}
Expand Down
11 changes: 10 additions & 1 deletion pallets/funding/src/tests/2_evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,12 @@ mod round_flow {

assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed);

// Check that on_idle has unlocked the failed bonds
// Cannot settle before the settlement starts
inst.settle_project(project_id).expect_err("Settlement should not be possible yet");

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.settle_project(project_id).unwrap();
inst.do_free_plmc_assertions(expected_evaluator_balances);
}
Expand Down Expand Up @@ -678,6 +683,10 @@ mod evaluate_extrinsic {

let treasury_account = <TestRuntime as Config>::ProtocolGrowthTreasury::get();
let pre_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_evaluation(
RuntimeOrigin::signed(EVALUATOR_4),
Expand Down
3 changes: 3 additions & 0 deletions pallets/funding/src/tests/3_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,9 @@ mod bid_extrinsic {
assert_eq!(bid_held_balance, frozen_amount);
assert_eq!(frozen_balance, frozen_amount);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_4), project_id, BIDDER_4, 0).unwrap();
});
Expand Down
3 changes: 3 additions & 0 deletions pallets/funding/src/tests/4_community.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,9 @@ mod community_contribute_extrinsic {
assert_eq!(bid_held_balance, frozen_amount);
assert_eq!(frozen_balance, frozen_amount);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0)
.unwrap();
Expand Down
3 changes: 3 additions & 0 deletions pallets/funding/src/tests/5_remainder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,9 @@ mod remaining_contribute_extrinsic {
};
assert_eq!(account_data, expected_account_data);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0)
.unwrap();
Expand Down
12 changes: 7 additions & 5 deletions pallets/funding/src/tests/6_funding_end.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod round_flow {

#[test]
fn evaluator_slash_is_decided() {
let (mut inst, project_id) = create_project_with_funding_percentage(20, None);
let (mut inst, project_id) = create_project_with_funding_percentage(20, None, true);
assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed);
assert_eq!(
inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome,
Expand All @@ -19,7 +19,7 @@ mod round_flow {
#[test]
fn evaluator_unchanged_is_decided() {
let (mut inst, project_id) =
create_project_with_funding_percentage(80, Some(FundingOutcomeDecision::AcceptFunding));
create_project_with_funding_percentage(80, Some(FundingOutcomeDecision::AcceptFunding), true);
assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful);
assert_eq!(
inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome,
Expand All @@ -29,7 +29,7 @@ mod round_flow {

#[test]
fn evaluator_reward_is_decided() {
let (mut inst, project_id) = create_project_with_funding_percentage(95, None);
let (mut inst, project_id) = create_project_with_funding_percentage(95, None, true);
let project_details = inst.get_project_details(project_id);
let project_metadata = inst.get_project_metadata(project_id);
assert_eq!(project_details.status, ProjectStatus::FundingSuccessful);
Expand Down Expand Up @@ -85,6 +85,7 @@ mod decide_project_outcome {
let _ = create_project_with_funding_percentage(
funding_percent,
Some(FundingOutcomeDecision::AcceptFunding),
true,
);
}
}
Expand All @@ -95,14 +96,15 @@ mod decide_project_outcome {
let _ = create_project_with_funding_percentage(
funding_percent,
Some(FundingOutcomeDecision::RejectFunding),
true,
);
}
}

#[test]
fn automatic_fail_less_eq_33_percent() {
for funding_percent in (1..=33).step_by(5) {
let _ = create_project_with_funding_percentage(funding_percent, None);
let _ = create_project_with_funding_percentage(funding_percent, None, true);
}
}

Expand Down Expand Up @@ -144,7 +146,7 @@ mod decide_project_outcome {
#[test]
fn automatic_success_bigger_eq_90_percent() {
for funding_percent in (90..=100).step_by(2) {
let _ = create_project_with_funding_percentage(funding_percent, None);
let _ = create_project_with_funding_percentage(funding_percent, None, true);
}
}
}
Expand Down
Loading

0 comments on commit 86996b7

Please sign in to comment.