Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/plmc-408-prevent-spam-on-project-creation #198

Merged
merged 6 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pallets/funding/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ mod benchmarks {
inst.mint_plmc_to(plmc_for_evaluating);

inst.advance_time(One::one()).unwrap();
inst.bond_for_users(project_id, evaluations).expect("All evaluations are accepted");
inst.evaluate_for_users(project_id, evaluations).expect("All evaluations are accepted");

run_blocks_to_execute_next_transition(project_id, UpdateType::EvaluationEnd, &mut inst);
inst.advance_time(1u32.into()).unwrap();
Expand Down Expand Up @@ -590,7 +590,7 @@ mod benchmarks {
inst.advance_time(One::one()).unwrap();

// do "x" evaluations for this user
inst.bond_for_users(test_project_id, existing_evaluations).expect("All evaluations are accepted");
inst.evaluate_for_users(test_project_id, existing_evaluations).expect("All evaluations are accepted");

let extrinsic_plmc_bonded = plmc_for_extrinsic_evaluation[0].plmc_amount;
let mut total_expected_plmc_bonded = BenchInstantiator::<T>::sum_balance_mappings(vec![
Expand Down Expand Up @@ -2533,7 +2533,7 @@ mod benchmarks {
inst.mint_plmc_to(plmc_for_evaluating);

inst.advance_time(One::one()).unwrap();
inst.bond_for_users(project_id, evaluations).expect("All evaluations are accepted");
inst.evaluate_for_users(project_id, evaluations).expect("All evaluations are accepted");

let evaluation_end_block =
inst.get_project_details(project_id).phase_transition_points.evaluation.end().unwrap();
Expand Down Expand Up @@ -2594,7 +2594,7 @@ mod benchmarks {
inst.mint_plmc_to(plmc_for_evaluating);

inst.advance_time(One::one()).unwrap();
inst.bond_for_users(project_id, evaluations).expect("All evaluations are accepted");
inst.evaluate_for_users(project_id, evaluations).expect("All evaluations are accepted");

let evaluation_end_block =
inst.get_project_details(project_id).phase_transition_points.evaluation.end().unwrap();
Expand Down
20 changes: 15 additions & 5 deletions pallets/funding/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@ impl<T: Config> Pallet<T> {
pub fn do_create(issuer: &AccountIdOf<T>, initial_metadata: ProjectMetadataOf<T>, did: Did) -> DispatchResult {
// * Get variables *
let project_id = NextProjectId::<T>::get();
let maybe_active_project = DidWithActiveProjects::<T>::get(did.clone());

// * Validity checks *
if let Some(metadata) = initial_metadata.offchain_information_hash {
ensure!(!Images::<T>::contains_key(metadata), Error::<T>::MetadataAlreadyExists);
}
ensure!(maybe_active_project == None, Error::<T>::IssuerHasActiveProjectAlready);

if let Err(error) = initial_metadata.is_valid() {
return match error {
Expand All @@ -95,7 +97,7 @@ impl<T: Config> Pallet<T> {
let now = <frame_system::Pallet<T>>::block_number();
let project_details = ProjectDetails {
issuer_account: issuer.clone(),
issuer_did: did,
issuer_did: did.clone(),
is_frozen: false,
weighted_average_price: None,
fundraising_target,
Expand Down Expand Up @@ -140,6 +142,7 @@ impl<T: Config> Pallet<T> {
if let Some(metadata) = initial_metadata.offchain_information_hash {
Images::<T>::insert(metadata, issuer);
}
DidWithActiveProjects::<T>::set(did, Some(project_id));

// * Emit events *
Self::deposit_event(Event::ProjectCreated { project_id, issuer: issuer.clone() });
Expand Down Expand Up @@ -305,7 +308,9 @@ impl<T: Config> Pallet<T> {
// * Update storage *
project_details.status = ProjectStatus::EvaluationFailed;
project_details.cleanup = Cleaner::Failure(CleanerState::Initialized(PhantomData::<Failure>));
ProjectsDetails::<T>::insert(project_id, project_details);
ProjectsDetails::<T>::insert(project_id, project_details.clone());
let issuer_did = project_details.issuer_did.clone();
DidWithActiveProjects::<T>::set(issuer_did, None);

// * Emit events *
Self::deposit_event(Event::EvaluationFailed { project_id });
Expand Down Expand Up @@ -517,7 +522,6 @@ impl<T: Config> Pallet<T> {
let mut project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
match calculation_result {
Err(pallet_error) if pallet_error == Error::<T>::NoBidsFound.into() => {
project_details.status = ProjectStatus::FundingFailed;
ProjectsDetails::<T>::insert(project_id, project_details);
let insertion_iterations = match Self::add_to_update_store(
<frame_system::Pallet<T>>::block_number() + 1u32.into(),
Expand Down Expand Up @@ -671,11 +675,15 @@ impl<T: Config> Pallet<T> {
let remaining_cts = project_details.remaining_contribution_tokens;
let remainder_end_block = project_details.phase_transition_points.remainder.end();
let now = <frame_system::Pallet<T>>::block_number();
let issuer_did = project_details.issuer_did.clone();

// * Validity checks *
ensure!(
// Can end due to running out of CTs
remaining_cts == Zero::zero() ||
project_details.status == ProjectStatus::FundingFailed ||
// or the auction being empty
project_details.status == ProjectStatus::AuctionRound(AuctionPhase::Candle) ||
// or the last funding round ending
matches!(remainder_end_block, Some(end_block) if now > end_block),
Error::<T>::TooEarlyForFundingEnd
);
Expand All @@ -700,6 +708,7 @@ impl<T: Config> Pallet<T> {
let funding_ratio = Perquintill::from_rational(funding_reached, funding_target);

// * Update Storage *
DidWithActiveProjects::<T>::set(issuer_did, None);
if funding_ratio <= Perquintill::from_percent(33u64) {
project_details.evaluation_round_info.evaluators_outcome = EvaluatorsOutcome::Slashed;
let insertion_iterations = Self::make_project_funding_fail(
Expand Down Expand Up @@ -2890,7 +2899,8 @@ impl<T: Config> Pallet<T> {
// the min_balance of funding assets (e.g USDT) are low enough so we don't expect users to care about their balance being dusted.
// We do think the UX would be bad if they cannot use all of their available tokens.
// Specially since a new funding asset account can be easily created by increasing the provider reference
T::FundingCurrency::transfer(asset_id, who, &fund_account, amount, Preservation::Expendable)?;
T::FundingCurrency::transfer(asset_id, who, &fund_account, amount, Preservation::Expendable)
.map_err(|_| Error::<T>::NotEnoughFunds)?;
JuaniRios marked this conversation as resolved.
Show resolved Hide resolved

Ok(())
}
Expand Down
11 changes: 7 additions & 4 deletions pallets/funding/src/instantiator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,7 +1056,7 @@ impl<
project_id
}

pub fn bond_for_users(
pub fn evaluate_for_users(
&mut self,
project_id: ProjectId,
bonds: Vec<UserToUSDBalance<T>>,
Expand Down Expand Up @@ -1116,7 +1116,7 @@ impl<
self.mint_plmc_to(plmc_eval_deposits.clone());
self.mint_plmc_to(plmc_existential_deposits.clone());

self.bond_for_users(project_id, evaluations).unwrap();
self.evaluate_for_users(project_id, evaluations).unwrap();

let expected_evaluator_balances =
Self::sum_balance_mappings(vec![plmc_eval_deposits.clone(), plmc_existential_deposits.clone()]);
Expand Down Expand Up @@ -1170,7 +1170,10 @@ impl<
// run on_initialize
self.advance_time(1u32.into()).unwrap();

assert_eq!(self.get_project_details(project_id).status, ProjectStatus::CommunityRound);
ensure!(
self.get_project_details(project_id).status == ProjectStatus::CommunityRound,
DispatchError::from("Auction failed")
);

Ok(())
}
Expand Down Expand Up @@ -1813,7 +1816,7 @@ pub mod async_features {
inst.mint_plmc_to(plmc_eval_deposits.clone());
inst.mint_plmc_to(plmc_existential_deposits.clone());

inst.bond_for_users(project_id, evaluations).unwrap();
inst.evaluate_for_users(project_id, evaluations).unwrap();

let expected_evaluator_balances =
Instantiator::<T, AllPalletsWithoutSystem, RuntimeEvent>::sum_balance_mappings(vec![
Expand Down
7 changes: 7 additions & 0 deletions pallets/funding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,10 @@ pub mod pallet {
ValueQuery,
>;

#[pallet::storage]
/// A map to keep track of what issuer's did has an active project. It prevents one issuer having multiple active projects
pub type DidWithActiveProjects<T: Config> = StorageMap<_, Blake2_128Concat, Did, ProjectId, OptionQuery>;

#[pallet::storage]
pub type DidWithWinningBids<T: Config> =
StorageDoubleMap<_, Blake2_128Concat, ProjectId, Blake2_128Concat, Did, bool, ValueQuery>;
Expand Down Expand Up @@ -974,6 +978,9 @@ pub mod pallet {
UserHasWinningBids,
// Round transition already happened.
RoundTransitionAlreadyHappened,
/// The issuer tried to create a new project but already has an active one
IssuerHasActiveProjectAlready,
NotEnoughFunds,
}

#[pallet::call]
Expand Down
Loading