Add an optional verifier to crowdloan (#2248)

* Add an optional verifier to crowdloan

* add tests

* verify signatures

* benchmark with signatures

* update crowdloan benchmark

* try to get keystore working in test

* rewrite to avoid traits

* Use MultiSignature and MultiSigner

* refactor and update benchmarks

* optimize check order

* fix no_std build

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Xiliang Chen
2021-02-26 20:26:54 +13:00
committed by GitHub
parent 49705026e0
commit 69734bb8ed
3 changed files with 207 additions and 59 deletions
+1
View File
@@ -5830,6 +5830,7 @@ dependencies = [
"sp-inherents", "sp-inherents",
"sp-io", "sp-io",
"sp-keyring", "sp-keyring",
"sp-keystore",
"sp-runtime", "sp-runtime",
"sp-session", "sp-session",
"sp-staking", "sp-staking",
+1
View File
@@ -51,6 +51,7 @@ sp-election-providers = { git = "https://github.com/paritytech/substrate", branc
pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-staking-reward-curve = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-staking-reward-curve = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
trie-db = "0.22.3" trie-db = "0.22.3"
serde_json = "1.0.61" serde_json = "1.0.61"
libsecp256k1 = "0.3.5" libsecp256k1 = "0.3.5"
+205 -59
View File
@@ -74,8 +74,11 @@ use frame_support::{
}, },
}; };
use frame_system::ensure_signed; use frame_system::ensure_signed;
use sp_runtime::{ModuleId, DispatchResult, use sp_runtime::{
traits::{AccountIdConversion, Hash, Saturating, Zero, CheckedAdd, Bounded} ModuleId, DispatchResult, MultiSignature, MultiSigner,
traits::{
AccountIdConversion, Hash, Saturating, Zero, CheckedAdd, Bounded, Verify, IdentifyAccount,
},
}; };
use crate::slots; use crate::slots;
use parity_scale_codec::{Encode, Decode}; use parity_scale_codec::{Encode, Decode};
@@ -140,6 +143,8 @@ pub struct FundInfo<AccountId, Balance, Hash, BlockNumber> {
parachain: Option<ParaId>, parachain: Option<ParaId>,
/// The owning account who placed the deposit. /// The owning account who placed the deposit.
owner: AccountId, owner: AccountId,
/// An optional verifier. If exists, contributions must be signed by verifier.
verifier: Option<MultiSigner>,
/// The amount of deposit placed. /// The amount of deposit placed.
deposit: Balance, deposit: Balance,
/// The total amount raised. /// The total amount raised.
@@ -256,6 +261,8 @@ decl_error! {
HasActiveParachain, HasActiveParachain,
/// The retirement period has not ended. /// The retirement period has not ended.
InRetirementPeriod, InRetirementPeriod,
/// Invalid signature.
InvalidSignature,
} }
} }
@@ -273,7 +280,8 @@ decl_module! {
#[compact] cap: BalanceOf<T>, #[compact] cap: BalanceOf<T>,
#[compact] first_slot: T::BlockNumber, #[compact] first_slot: T::BlockNumber,
#[compact] last_slot: T::BlockNumber, #[compact] last_slot: T::BlockNumber,
#[compact] end: T::BlockNumber #[compact] end: T::BlockNumber,
verifier: Option<MultiSigner>,
) { ) {
let owner = ensure_signed(origin)?; let owner = ensure_signed(origin)?;
@@ -291,6 +299,7 @@ decl_module! {
<Funds<T>>::insert(index, FundInfo { <Funds<T>>::insert(index, FundInfo {
parachain: None, parachain: None,
owner, owner,
verifier,
deposit, deposit,
raised: Zero::zero(), raised: Zero::zero(),
end, end,
@@ -307,8 +316,14 @@ decl_module! {
/// Contribute to a crowd sale. This will transfer some balance over to fund a parachain /// Contribute to a crowd sale. This will transfer some balance over to fund a parachain
/// slot. It will be withdrawable in two instances: the parachain becomes retired; or the /// slot. It will be withdrawable in two instances: the parachain becomes retired; or the
/// slot is unable to be purchased and the timeout expires. /// slot is unable to be purchased and the timeout expires.
/// A valid signature maybe required in order to accept the contribution.
#[weight = 0] #[weight = 0]
fn contribute(origin, #[compact] index: FundIndex, #[compact] value: BalanceOf<T>) { fn contribute(
origin,
#[compact] index: FundIndex,
#[compact] value: BalanceOf<T>,
signature: Option<MultiSignature>
) {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
ensure!(value >= T::MinContribution::get(), Error::<T>::ContributionTooSmall); ensure!(value >= T::MinContribution::get(), Error::<T>::ContributionTooSmall);
@@ -320,10 +335,18 @@ decl_module! {
let now = <frame_system::Module<T>>::block_number(); let now = <frame_system::Module<T>>::block_number();
ensure!(fund.end > now, Error::<T>::ContributionPeriodOver); ensure!(fund.end > now, Error::<T>::ContributionPeriodOver);
let old_balance = Self::contribution_get(index, &who);
if let Some(ref verifier) = fund.verifier {
let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
let payload = (index, &who, old_balance, value);
let valid = payload.using_encoded(|encoded| signature.verify(encoded, &verifier.clone().into_account()));
ensure!(valid, Error::<T>::InvalidSignature);
}
T::Currency::transfer(&who, &Self::fund_account_id(index), value, AllowDeath)?; T::Currency::transfer(&who, &Self::fund_account_id(index), value, AllowDeath)?;
let balance = Self::contribution_get(index, &who); let balance = old_balance.saturating_add(value);
let balance = balance.saturating_add(value);
Self::contribution_put(index, &who, &balance); Self::contribution_put(index, &who, &balance);
if <slots::Module<T>>::is_ending(now).is_some() { if <slots::Module<T>>::is_ending(now).is_some() {
@@ -445,7 +468,7 @@ decl_module! {
/// Withdraw full balance of a contributor to an unsuccessful or off-boarded fund. /// Withdraw full balance of a contributor to an unsuccessful or off-boarded fund.
#[weight = 0] #[weight = 0]
fn withdraw(origin, who: T::AccountId, #[compact] index: FundIndex) { fn withdraw(origin, who: T::AccountId, #[compact] index: FundIndex) {
ensure_signed(origin)?; let _ = ensure_signed(origin)?;
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?; let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?;
ensure!(fund.parachain.is_none(), Error::<T>::FundNotRetired); ensure!(fund.parachain.is_none(), Error::<T>::FundNotRetired);
@@ -576,11 +599,32 @@ impl<T: Config> Module<T> {
} }
} }
#[cfg(any(feature = "runtime-benchmarks", test))]
mod crypto {
use sp_core::ed25519;
use sp_io::crypto::{ed25519_sign, ed25519_generate};
use sp_std::{
vec::Vec,
convert::TryFrom,
};
use sp_runtime::{MultiSigner, MultiSignature};
pub fn create_ed25519_pubkey(seed: Vec<u8>) -> MultiSigner {
ed25519_generate(0.into(), Some(seed)).into()
}
pub fn create_ed25519_signature(payload: &[u8], pubkey: MultiSigner) -> MultiSignature {
let edpubkey = ed25519::Public::try_from(pubkey).unwrap();
let edsig = ed25519_sign(0.into(), &edpubkey, payload).unwrap();
edsig.into()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::{collections::HashMap, cell::RefCell}; use std::{collections::HashMap, cell::RefCell, sync::Arc};
use frame_support::{ use frame_support::{
assert_ok, assert_noop, parameter_types, assert_ok, assert_noop, parameter_types,
traits::{OnInitialize, OnFinalize}, traits::{OnInitialize, OnFinalize},
@@ -593,6 +637,7 @@ mod tests {
Permill, testing::Header, Permill, testing::Header,
traits::{BlakeTwo256, IdentityLookup}, traits::{BlakeTwo256, IdentityLookup},
}; };
use sp_keystore::{KeystoreExt, testing::KeyStore};
use crate::slots::{self, Registrar}; use crate::slots::{self, Registrar};
use crate::crowdloan; use crate::crowdloan;
@@ -769,7 +814,10 @@ mod tests {
pallet_balances::GenesisConfig::<Test>{ pallet_balances::GenesisConfig::<Test>{
balances: vec![(1, 1000), (2, 2000), (3, 3000), (4, 4000)], balances: vec![(1, 1000), (2, 2000), (3, 3000), (4, 4000)],
}.assimilate_storage(&mut t).unwrap(); }.assimilate_storage(&mut t).unwrap();
t.into() let keystore = KeyStore::new();
let mut t: sp_io::TestExternalities = t.into();
t.register_extension(KeystoreExt(Arc::new(keystore)));
t
} }
fn run_to_block(n: u64) { fn run_to_block(n: u64) {
@@ -805,12 +853,46 @@ mod tests {
fn create_works() { fn create_works() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Now try to create a crowdloan campaign // Now try to create a crowdloan campaign
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Crowdloan::fund_count(), 1); assert_eq!(Crowdloan::fund_count(), 1);
// This is what the initial `fund_info` should look like // This is what the initial `fund_info` should look like
let fund_info = FundInfo { let fund_info = FundInfo {
parachain: None, parachain: None,
owner: 1, owner: 1,
verifier: None,
deposit: 1,
raised: 0,
// 5 blocks length + 3 block ending period + 1 starting block
end: 9,
cap: 1000,
last_contribution: LastContribution::Never,
first_slot: 1,
last_slot: 4,
deploy_data: None,
};
assert_eq!(Crowdloan::funds(0), Some(fund_info));
// User has deposit removed from their free balance
assert_eq!(Balances::free_balance(1), 999);
// Deposit is placed in crowdloan free balance
assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(0)), 1);
// No new raise until first contribution
let empty: Vec<FundIndex> = Vec::new();
assert_eq!(Crowdloan::new_raise(), empty);
});
}
#[test]
fn create_with_verifier_works() {
new_test_ext().execute_with(|| {
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
// Now try to create a crowdloan campaign
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, Some(pubkey.clone())));
assert_eq!(Crowdloan::fund_count(), 1);
// This is what the initial `fund_info` should look like
let fund_info = FundInfo {
parachain: None,
owner: 1,
verifier: Some(pubkey),
deposit: 1, deposit: 1,
raised: 0, raised: 0,
// 5 blocks length + 3 block ending period + 1 starting block // 5 blocks length + 3 block ending period + 1 starting block
@@ -837,17 +919,17 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Cannot create a crowdloan with bad slots // Cannot create a crowdloan with bad slots
assert_noop!( assert_noop!(
Crowdloan::create(Origin::signed(1), 1000, 4, 1, 9), Crowdloan::create(Origin::signed(1), 1000, 4, 1, 9, None),
Error::<Test>::LastSlotBeforeFirstSlot Error::<Test>::LastSlotBeforeFirstSlot
); );
assert_noop!( assert_noop!(
Crowdloan::create(Origin::signed(1), 1000, 1, 5, 9), Crowdloan::create(Origin::signed(1), 1000, 1, 5, 9, None),
Error::<Test>::LastSlotTooFarInFuture Error::<Test>::LastSlotTooFarInFuture
); );
// Cannot create a crowdloan without some deposit funds // Cannot create a crowdloan without some deposit funds
assert_noop!( assert_noop!(
Crowdloan::create(Origin::signed(1337), 1000, 1, 3, 9), Crowdloan::create(Origin::signed(1337), 1000, 1, 3, 9, None),
BalancesError::<Test, _>::InsufficientBalance BalancesError::<Test, _>::InsufficientBalance
); );
}); });
@@ -857,7 +939,7 @@ mod tests {
fn contribute_works() { fn contribute_works() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Balances::free_balance(1), 999); assert_eq!(Balances::free_balance(1), 999);
assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(0)), 1); assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(0)), 1);
@@ -865,7 +947,7 @@ mod tests {
assert_eq!(Crowdloan::contribution_get(0, &1), 0); assert_eq!(Crowdloan::contribution_get(0, &1), 0);
// User 1 contributes to their own crowdloan // User 1 contributes to their own crowdloan
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49, None));
// User 1 has spent some funds to do this, transfer fees **are** taken // User 1 has spent some funds to do this, transfer fees **are** taken
assert_eq!(Balances::free_balance(1), 950); assert_eq!(Balances::free_balance(1), 950);
// Contributions are stored in the trie // Contributions are stored in the trie
@@ -883,26 +965,70 @@ mod tests {
}); });
} }
#[test]
fn contribute_with_verifier_works() {
new_test_ext().execute_with(|| {
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, Some(pubkey.clone())));
assert_eq!(Balances::free_balance(1), 999);
assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(0)), 1);
// Missing signature
assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49, None), Error::<Test>::InvalidSignature);
let payload = (0u32, 1u64, 0u64, 49u64);
let valid_signature = crypto::create_ed25519_signature(&payload.encode(), pubkey.clone());
let invalid_signature = MultiSignature::default();
// Invalid signature
assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49, Some(invalid_signature)), Error::<Test>::InvalidSignature);
// Valid signature wrong parameter
assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 50, Some(valid_signature.clone())), Error::<Test>::InvalidSignature);
assert_noop!(Crowdloan::contribute(Origin::signed(2), 0, 49, Some(valid_signature.clone())), Error::<Test>::InvalidSignature);
// Valid signature
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49, Some(valid_signature.clone())));
// Reuse valid signature
assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49, Some(valid_signature)), Error::<Test>::InvalidSignature);
let payload_2 = (0u32, 1u64, 49u64, 10u64);
let valid_signature_2 = crypto::create_ed25519_signature(&payload_2.encode(), pubkey);
// New valid signature
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 10, Some(valid_signature_2)));
// Contributions appear in free balance of crowdloan
assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(0)), 60);
// Contribution amount is correct
let fund = Crowdloan::funds(0).unwrap();
assert_eq!(fund.raised, 59);
});
}
#[test] #[test]
fn contribute_handles_basic_errors() { fn contribute_handles_basic_errors() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Cannot contribute to non-existing fund // Cannot contribute to non-existing fund
assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49), Error::<Test>::InvalidFundIndex); assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49, None), Error::<Test>::InvalidFundIndex);
// Cannot contribute below minimum contribution // Cannot contribute below minimum contribution
assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 9), Error::<Test>::ContributionTooSmall); assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 9, None), Error::<Test>::ContributionTooSmall);
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 101)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 101, None));
// Cannot contribute past the limit // Cannot contribute past the limit
assert_noop!(Crowdloan::contribute(Origin::signed(2), 0, 900), Error::<Test>::CapExceeded); assert_noop!(Crowdloan::contribute(Origin::signed(2), 0, 900, None), Error::<Test>::CapExceeded);
// Move past end date // Move past end date
run_to_block(10); run_to_block(10);
// Cannot contribute to ended fund // Cannot contribute to ended fund
assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49), Error::<Test>::ContributionPeriodOver); assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49, None), Error::<Test>::ContributionPeriodOver);
}); });
} }
@@ -910,7 +1036,7 @@ mod tests {
fn fix_deploy_data_works() { fn fix_deploy_data_works() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Balances::free_balance(1), 999); assert_eq!(Balances::free_balance(1), 999);
// Add deploy data // Add deploy data
@@ -940,7 +1066,7 @@ mod tests {
fn fix_deploy_data_handles_basic_errors() { fn fix_deploy_data_handles_basic_errors() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Balances::free_balance(1), 999); assert_eq!(Balances::free_balance(1), 999);
// Cannot set deploy data by non-owner // Cannot set deploy data by non-owner
@@ -988,7 +1114,7 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Balances::free_balance(1), 999); assert_eq!(Balances::free_balance(1), 999);
// Add deploy data // Add deploy data
@@ -1001,7 +1127,7 @@ mod tests {
)); ));
// Fund crowdloan // Fund crowdloan
assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000)); assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000, None));
run_to_block(10); run_to_block(10);
@@ -1024,11 +1150,11 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Balances::free_balance(1), 999); assert_eq!(Balances::free_balance(1), 999);
// Fund crowdloan // Fund crowdloan
assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000)); assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000, None));
run_to_block(10); run_to_block(10);
@@ -1062,7 +1188,7 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Balances::free_balance(1), 999); assert_eq!(Balances::free_balance(1), 999);
// Add deploy data // Add deploy data
@@ -1075,7 +1201,7 @@ mod tests {
)); ));
// Fund crowdloan // Fund crowdloan
assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000)); assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000, None));
run_to_block(10); run_to_block(10);
@@ -1105,7 +1231,7 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
assert_eq!(Balances::free_balance(1), 999); assert_eq!(Balances::free_balance(1), 999);
// Add deploy data // Add deploy data
@@ -1118,7 +1244,7 @@ mod tests {
)); ));
// Fund crowdloan // Fund crowdloan
assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000)); assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000, None));
run_to_block(10); run_to_block(10);
@@ -1150,11 +1276,11 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
// Transfer fee is taken here // Transfer fee is taken here
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200)); assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300)); assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, None));
// Skip all the way to the end // Skip all the way to the end
run_to_block(50); run_to_block(50);
@@ -1176,9 +1302,9 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
// Transfer fee is taken here // Transfer fee is taken here
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49, None));
assert_eq!(Balances::free_balance(1), 950); assert_eq!(Balances::free_balance(1), 950);
run_to_block(5); run_to_block(5);
@@ -1200,11 +1326,11 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
// Transfer fee is taken here // Transfer fee is taken here
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200)); assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300)); assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, None));
// Skip all the way to the end // Skip all the way to the end
run_to_block(50); run_to_block(50);
@@ -1238,12 +1364,12 @@ mod tests {
ext.execute_with(|| { ext.execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 100_000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 100_000, 1, 4, 9, None));
// Add lots of contributors, beyond what we can delete in one go. // Add lots of contributors, beyond what we can delete in one go.
for i in 0 .. 30 { for i in 0 .. 30 {
Balances::make_free_balance_be(&i, 300); Balances::make_free_balance_be(&i, 300);
assert_ok!(Crowdloan::contribute(Origin::signed(i), 0, 100)); assert_ok!(Crowdloan::contribute(Origin::signed(i), 0, 100, None));
assert_eq!(Crowdloan::contribution_get(0, &i), 100); assert_eq!(Crowdloan::contribution_get(0, &i), 100);
} }
@@ -1302,11 +1428,11 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Set up a crowdloan // Set up a crowdloan
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
// Transfer fee is taken here // Transfer fee is taken here
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200)); assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300)); assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, None));
// Cannot dissolve an invalid fund index // Cannot dissolve an invalid fund index
assert_noop!(Crowdloan::dissolve(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex); assert_noop!(Crowdloan::dissolve(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex);
@@ -1334,9 +1460,9 @@ mod tests {
fn fund_before_auction_works() { fn fund_before_auction_works() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// Create a crowdloan before an auction is created // Create a crowdloan before an auction is created
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None));
// Users can already contribute // Users can already contribute
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49, None));
// Fund added to NewRaise // Fund added to NewRaise
assert_eq!(Crowdloan::new_raise(), vec![0]); assert_eq!(Crowdloan::new_raise(), vec![0]);
@@ -1376,12 +1502,12 @@ mod tests {
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
// Create two competing crowdloans, with end dates across multiple auctions // Create two competing crowdloans, with end dates across multiple auctions
// Each crowdloan is competing for the same slots, so only one can win // Each crowdloan is competing for the same slots, so only one can win
assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 30)); assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 30, None));
assert_ok!(Crowdloan::create(Origin::signed(2), 1000, 1, 4, 30)); assert_ok!(Crowdloan::create(Origin::signed(2), 1000, 1, 4, 30, None));
// Contribute to all, but more money to 0, less to 1 // Contribute to all, but more money to 0, less to 1
assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 300)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 300, None));
assert_ok!(Crowdloan::contribute(Origin::signed(1), 1, 200)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 1, 200, None));
// Add deploy data to all // Add deploy data to all
assert_ok!(Crowdloan::fix_deploy_data( assert_ok!(Crowdloan::fix_deploy_data(
@@ -1413,7 +1539,7 @@ mod tests {
// Create a second auction // Create a second auction
assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
// Contribute to existing funds add to NewRaise // Contribute to existing funds add to NewRaise
assert_ok!(Crowdloan::contribute(Origin::signed(1), 1, 10)); assert_ok!(Crowdloan::contribute(Origin::signed(1), 1, 10, None));
// End the current auction, fund 1 wins! // End the current auction, fund 1 wins!
run_to_block(20); run_to_block(20);
@@ -1464,14 +1590,22 @@ mod benchmarking {
let caller = account("fund_creator", 0, 0); let caller = account("fund_creator", 0, 0);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value()); T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
assert_ok!(Crowdloan::<T>::create(RawOrigin::Signed(caller).into(), cap, first_slot, last_slot, end)); // Assume ed25519 is most complex signature format
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
assert_ok!(Crowdloan::<T>::create(RawOrigin::Signed(caller).into(), cap, first_slot, last_slot, end, Some(pubkey)));
FundCount::get() - 1 FundCount::get() - 1
} }
fn contribute_fund<T: Config>(who: &T::AccountId, index: FundIndex) { fn contribute_fund<T: Config>(who: &T::AccountId, index: FundIndex) {
T::Currency::make_free_balance_be(&who, BalanceOf::<T>::max_value()); T::Currency::make_free_balance_be(&who, BalanceOf::<T>::max_value());
let value = T::MinContribution::get(); let value = T::MinContribution::get();
assert_ok!(Crowdloan::<T>::contribute(RawOrigin::Signed(who.clone()).into(), index, value));
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
let payload = (index, &who, BalanceOf::<T>::default(), value);
let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey);
assert_ok!(Crowdloan::<T>::contribute(RawOrigin::Signed(who.clone()).into(), index, value, Some(sig)));
} }
fn worst_validation_code<T: Config>() -> Vec<u8> { fn worst_validation_code<T: Config>() -> Vec<u8> {
@@ -1540,9 +1674,10 @@ mod benchmarking {
let end = T::BlockNumber::max_value(); let end = T::BlockNumber::max_value();
let caller: T::AccountId = whitelisted_caller(); let caller: T::AccountId = whitelisted_caller();
let verifier = account("verifier", 0, 0);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value()); T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
}: _(RawOrigin::Signed(caller), cap, first_slot, last_slot, end) }: _(RawOrigin::Signed(caller), cap, first_slot, last_slot, end, Some(verifier))
verify { verify {
assert_last_event::<T>(RawEvent::Created(FundCount::get() - 1).into()) assert_last_event::<T>(RawEvent::Created(FundCount::get() - 1).into())
} }
@@ -1553,7 +1688,12 @@ mod benchmarking {
let caller: T::AccountId = whitelisted_caller(); let caller: T::AccountId = whitelisted_caller();
let contribution = T::MinContribution::get(); let contribution = T::MinContribution::get();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value()); T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
}: _(RawOrigin::Signed(caller.clone()), fund_index, contribution)
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
let payload = (fund_index, &caller, BalanceOf::<T>::default(), contribution);
let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey);
}: _(RawOrigin::Signed(caller.clone()), fund_index, contribution, Some(sig))
verify { verify {
// NewRaise is appended to, so we don't need to fill it up for worst case scenario. // NewRaise is appended to, so we don't need to fill it up for worst case scenario.
assert!(!NewRaise::get().is_empty()); assert!(!NewRaise::get().is_empty());
@@ -1638,12 +1778,18 @@ mod benchmarking {
let n in 2 .. 100; let n in 2 .. 100;
let end_block: T::BlockNumber = 100u32.into(); let end_block: T::BlockNumber = 100u32.into();
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
for i in 0 .. n { for i in 0 .. n {
let fund_index = create_fund::<T>(end_block); let fund_index = create_fund::<T>(end_block);
let contributor: T::AccountId = account("contributor", i, 0); let contributor: T::AccountId = account("contributor", i, 0);
let contribution = T::MinContribution::get() * (i + 1).into(); let contribution = T::MinContribution::get() * (i + 1).into();
T::Currency::make_free_balance_be(&contributor, BalanceOf::<T>::max_value()); T::Currency::make_free_balance_be(&contributor, BalanceOf::<T>::max_value());
Crowdloan::<T>::contribute(RawOrigin::Signed(contributor).into(), fund_index, contribution)?;
let payload = (fund_index, &contributor, BalanceOf::<T>::default(), contribution);
let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey.clone());
Crowdloan::<T>::contribute(RawOrigin::Signed(contributor).into(), fund_index, contribution, Some(sig))?;
} }
let lease_period_index = end_block / T::LeasePeriod::get(); let lease_period_index = end_block / T::LeasePeriod::get();