From 85e972f035d087fba0747284d75e17e955ec4c02 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 7 Nov 2021 07:15:31 -0400 Subject: [PATCH 1/4] Add contirbute all, do not allow death on contribute --- runtime/common/src/crowdloan.rs | 180 ++++++++++++++++++-------------- 1 file changed, 103 insertions(+), 77 deletions(-) diff --git a/runtime/common/src/crowdloan.rs b/runtime/common/src/crowdloan.rs index d496617730fa..6acd7abad12b 100644 --- a/runtime/common/src/crowdloan.rs +++ b/runtime/common/src/crowdloan.rs @@ -55,9 +55,13 @@ use crate::{ }; use frame_support::{ ensure, - pallet_prelude::Weight, + pallet_prelude::{Weight, DispatchResult}, storage::{child, ChildTriePrefixIterator}, - traits::{Currency, ExistenceRequirement::AllowDeath, Get, ReservableCurrency}, + traits::{ + Currency, + ExistenceRequirement::{self, AllowDeath, KeepAlive}, + Get, ReservableCurrency, + }, Identity, PalletId, }; pub use pallet::*; @@ -444,81 +448,7 @@ pub mod pallet { signature: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; - - ensure!(value >= T::MinContribution::get(), Error::::ContributionTooSmall); - let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; - fund.raised = fund.raised.checked_add(&value).ok_or(Error::::Overflow)?; - ensure!(fund.raised <= fund.cap, Error::::CapExceeded); - - // Make sure crowdloan has not ended - let now = >::block_number(); - ensure!(now < fund.end, Error::::ContributionPeriodOver); - - // Make sure crowdloan is in a valid lease period - let now = frame_system::Pallet::::block_number(); - let (current_lease_period, _) = - T::Auctioneer::lease_period_index(now).ok_or(Error::::NoLeasePeriod)?; - ensure!(current_lease_period <= fund.first_period, Error::::ContributionPeriodOver); - - // Make sure crowdloan has not already won. - let fund_account = Self::fund_account_id(index); - ensure!( - !T::Auctioneer::has_won_an_auction(index, &fund_account), - Error::::BidOrLeaseActive - ); - - // We disallow any crowdloan contributions during the VRF Period, so that people do not sneak their - // contributions into the auction when it would not impact the outcome. - ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::::VrfDelayInProgress); - - let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who); - - if let Some(ref verifier) = fund.verifier { - let signature = signature.ok_or(Error::::InvalidSignature)?; - let payload = (index, &who, old_balance, value); - let valid = payload.using_encoded(|encoded| { - signature.verify(encoded, &verifier.clone().into_account()) - }); - ensure!(valid, Error::::InvalidSignature); - } - - CurrencyOf::::transfer(&who, &fund_account, value, AllowDeath)?; - - let balance = old_balance.saturating_add(value); - Self::contribution_put(fund.trie_index, &who, &balance, &memo); - - if T::Auctioneer::auction_status(now).is_ending().is_some() { - match fund.last_contribution { - // In ending period; must ensure that we are in NewRaise. - LastContribution::Ending(n) if n == now => { - // do nothing - already in NewRaise - }, - _ => { - NewRaise::::append(index); - fund.last_contribution = LastContribution::Ending(now); - }, - } - } else { - let endings_count = Self::endings_count(); - match fund.last_contribution { - LastContribution::PreEnding(a) if a == endings_count => { - // Not in ending period and no auctions have ended ending since our - // previous bid which was also not in an ending period. - // `NewRaise` will contain our ID still: Do nothing. - }, - _ => { - // Not in ending period; but an auction has been ending since our previous - // bid, or we never had one to begin with. Add bid. - NewRaise::::append(index); - fund.last_contribution = LastContribution::PreEnding(endings_count); - }, - } - } - - Funds::::insert(index, &fund); - - Self::deposit_event(Event::::Contributed(who, index, value)); - Ok(()) + Self::do_contribute(who, index, value, signature, KeepAlive) } /// Withdraw full balance of a specific contributor. @@ -706,6 +636,19 @@ pub mod pallet { Self::deposit_event(Event::::AddedToNewRaise(index)); Ok(()) } + + /// Contribute your entire balance to a crowd sale. This will transfer the entire balance of a user over to fund a parachain + /// slot. It will be withdrawable when the crowdloan has ended and the funds are unused. + #[pallet::weight(T::WeightInfo::contribute())] + pub fn contribute_all( + origin: OriginFor, + #[pallet::compact] index: ParaId, + signature: Option, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let value = CurrencyOf::::free_balance(&who); + Self::do_contribute(who, index, value, signature, AllowDeath) + } } } @@ -784,6 +727,89 @@ impl Pallet { Ok(()) } + + fn do_contribute( + who: T::AccountId, + index: ParaId, + value: BalanceOf, + signature: Option, + existence: ExistenceRequirement, + ) -> DispatchResult { + ensure!(value >= T::MinContribution::get(), Error::::ContributionTooSmall); + let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; + fund.raised = fund.raised.checked_add(&value).ok_or(Error::::Overflow)?; + ensure!(fund.raised <= fund.cap, Error::::CapExceeded); + + // Make sure crowdloan has not ended + let now = >::block_number(); + ensure!(now < fund.end, Error::::ContributionPeriodOver); + + // Make sure crowdloan is in a valid lease period + let now = frame_system::Pallet::::block_number(); + let (current_lease_period, _) = + T::Auctioneer::lease_period_index(now).ok_or(Error::::NoLeasePeriod)?; + ensure!(current_lease_period <= fund.first_period, Error::::ContributionPeriodOver); + + // Make sure crowdloan has not already won. + let fund_account = Self::fund_account_id(index); + ensure!( + !T::Auctioneer::has_won_an_auction(index, &fund_account), + Error::::BidOrLeaseActive + ); + + // We disallow any crowdloan contributions during the VRF Period, so that people do not sneak their + // contributions into the auction when it would not impact the outcome. + ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::::VrfDelayInProgress); + + let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who); + + if let Some(ref verifier) = fund.verifier { + let signature = signature.ok_or(Error::::InvalidSignature)?; + let payload = (index, &who, old_balance, value); + let valid = payload.using_encoded(|encoded| { + signature.verify(encoded, &verifier.clone().into_account()) + }); + ensure!(valid, Error::::InvalidSignature); + } + + CurrencyOf::::transfer(&who, &fund_account, value, existence)?; + + let balance = old_balance.saturating_add(value); + Self::contribution_put(fund.trie_index, &who, &balance, &memo); + + if T::Auctioneer::auction_status(now).is_ending().is_some() { + match fund.last_contribution { + // In ending period; must ensure that we are in NewRaise. + LastContribution::Ending(n) if n == now => { + // do nothing - already in NewRaise + }, + _ => { + NewRaise::::append(index); + fund.last_contribution = LastContribution::Ending(now); + }, + } + } else { + let endings_count = Self::endings_count(); + match fund.last_contribution { + LastContribution::PreEnding(a) if a == endings_count => { + // Not in ending period and no auctions have ended ending since our + // previous bid which was also not in an ending period. + // `NewRaise` will contain our ID still: Do nothing. + }, + _ => { + // Not in ending period; but an auction has been ending since our previous + // bid, or we never had one to begin with. Add bid. + NewRaise::::append(index); + fund.last_contribution = LastContribution::PreEnding(endings_count); + }, + } + } + + Funds::::insert(index, &fund); + + Self::deposit_event(Event::::Contributed(who, index, value)); + Ok(()) + } } impl crate::traits::OnSwap for Pallet { From c4746e336ac105b1318d8ec6f4cebf504f5d2fba Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 7 Nov 2021 14:08:49 -0400 Subject: [PATCH 2/4] fmt --- node/network/availability-distribution/src/requester/mod.rs | 2 +- node/network/collator-protocol/src/validator_side/mod.rs | 2 +- node/network/statement-distribution/src/tests.rs | 2 +- runtime/common/src/crowdloan.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/network/availability-distribution/src/requester/mod.rs b/node/network/availability-distribution/src/requester/mod.rs index f678a768d61f..53804aae8723 100644 --- a/node/network/availability-distribution/src/requester/mod.rs +++ b/node/network/availability-distribution/src/requester/mod.rs @@ -159,7 +159,7 @@ impl Requester { // Just book keeping - we are already requesting that chunk: { e.get_mut().add_leaf(leaf); - } + }, Entry::Vacant(e) => { let tx = self.tx.clone(); let metrics = self.metrics.clone(); diff --git a/node/network/collator-protocol/src/validator_side/mod.rs b/node/network/collator-protocol/src/validator_side/mod.rs index f7672d932dcd..6eb92f5f81e7 100644 --- a/node/network/collator-protocol/src/validator_side/mod.rs +++ b/node/network/collator-protocol/src/validator_side/mod.rs @@ -1462,7 +1462,7 @@ async fn poll_collation_response( ); CollationFetchResult::Error(COST_WRONG_PARA) - } + }, Ok(CollationFetchingResponse::Collation(receipt, pov)) => { tracing::debug!( target: LOG_TARGET, diff --git a/node/network/statement-distribution/src/tests.rs b/node/network/statement-distribution/src/tests.rs index 2c41d5e7ddf0..80fdb50a4f26 100644 --- a/node/network/statement-distribution/src/tests.rs +++ b/node/network/statement-distribution/src/tests.rs @@ -1814,7 +1814,7 @@ fn peer_cant_flood_with_large_statements() { if p == peer_a && r == COST_APPARENT_FLOOD => { punished = true; - } + }, m => panic!("Unexpected message: {:?}", m), } diff --git a/runtime/common/src/crowdloan.rs b/runtime/common/src/crowdloan.rs index 6acd7abad12b..6087bc9ab08c 100644 --- a/runtime/common/src/crowdloan.rs +++ b/runtime/common/src/crowdloan.rs @@ -55,7 +55,7 @@ use crate::{ }; use frame_support::{ ensure, - pallet_prelude::{Weight, DispatchResult}, + pallet_prelude::{DispatchResult, Weight}, storage::{child, ChildTriePrefixIterator}, traits::{ Currency, From 3cc0f09728cebfeed30accf7ed1700626fec207c Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 7 Nov 2021 14:24:58 -0400 Subject: [PATCH 3/4] Revert "fmt" This reverts commit c4746e336ac105b1318d8ec6f4cebf504f5d2fba. --- node/network/availability-distribution/src/requester/mod.rs | 2 +- node/network/collator-protocol/src/validator_side/mod.rs | 2 +- node/network/statement-distribution/src/tests.rs | 2 +- runtime/common/src/crowdloan.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/network/availability-distribution/src/requester/mod.rs b/node/network/availability-distribution/src/requester/mod.rs index 53804aae8723..f678a768d61f 100644 --- a/node/network/availability-distribution/src/requester/mod.rs +++ b/node/network/availability-distribution/src/requester/mod.rs @@ -159,7 +159,7 @@ impl Requester { // Just book keeping - we are already requesting that chunk: { e.get_mut().add_leaf(leaf); - }, + } Entry::Vacant(e) => { let tx = self.tx.clone(); let metrics = self.metrics.clone(); diff --git a/node/network/collator-protocol/src/validator_side/mod.rs b/node/network/collator-protocol/src/validator_side/mod.rs index 6eb92f5f81e7..f7672d932dcd 100644 --- a/node/network/collator-protocol/src/validator_side/mod.rs +++ b/node/network/collator-protocol/src/validator_side/mod.rs @@ -1462,7 +1462,7 @@ async fn poll_collation_response( ); CollationFetchResult::Error(COST_WRONG_PARA) - }, + } Ok(CollationFetchingResponse::Collation(receipt, pov)) => { tracing::debug!( target: LOG_TARGET, diff --git a/node/network/statement-distribution/src/tests.rs b/node/network/statement-distribution/src/tests.rs index 80fdb50a4f26..2c41d5e7ddf0 100644 --- a/node/network/statement-distribution/src/tests.rs +++ b/node/network/statement-distribution/src/tests.rs @@ -1814,7 +1814,7 @@ fn peer_cant_flood_with_large_statements() { if p == peer_a && r == COST_APPARENT_FLOOD => { punished = true; - }, + } m => panic!("Unexpected message: {:?}", m), } diff --git a/runtime/common/src/crowdloan.rs b/runtime/common/src/crowdloan.rs index 6087bc9ab08c..6acd7abad12b 100644 --- a/runtime/common/src/crowdloan.rs +++ b/runtime/common/src/crowdloan.rs @@ -55,7 +55,7 @@ use crate::{ }; use frame_support::{ ensure, - pallet_prelude::{DispatchResult, Weight}, + pallet_prelude::{Weight, DispatchResult}, storage::{child, ChildTriePrefixIterator}, traits::{ Currency, From d9e12f21c5e016543e63572151a77f3c7c47fc44 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 7 Nov 2021 14:25:21 -0400 Subject: [PATCH 4/4] fmt --- runtime/common/src/crowdloan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/common/src/crowdloan.rs b/runtime/common/src/crowdloan.rs index 6acd7abad12b..6087bc9ab08c 100644 --- a/runtime/common/src/crowdloan.rs +++ b/runtime/common/src/crowdloan.rs @@ -55,7 +55,7 @@ use crate::{ }; use frame_support::{ ensure, - pallet_prelude::{Weight, DispatchResult}, + pallet_prelude::{DispatchResult, Weight}, storage::{child, ChildTriePrefixIterator}, traits::{ Currency,