From 69734bb8ed76c57135deea799b0cd566bf78546d Mon Sep 17 00:00:00 2001 From: Xiliang Chen Date: Fri, 26 Feb 2021 20:26:54 +1300 Subject: [PATCH] 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 --- polkadot/Cargo.lock | 1 + polkadot/runtime/common/Cargo.toml | 1 + polkadot/runtime/common/src/crowdloan.rs | 264 ++++++++++++++++++----- 3 files changed, 207 insertions(+), 59 deletions(-) diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index 10dcd08ba4..4e8060f576 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -5830,6 +5830,7 @@ dependencies = [ "sp-inherents", "sp-io", "sp-keyring", + "sp-keystore", "sp-runtime", "sp-session", "sp-staking", diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 5e0f75691e..ec272f5e70 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -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-staking-reward-curve = { 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" serde_json = "1.0.61" libsecp256k1 = "0.3.5" diff --git a/polkadot/runtime/common/src/crowdloan.rs b/polkadot/runtime/common/src/crowdloan.rs index 213566d36c..b68c51aa67 100644 --- a/polkadot/runtime/common/src/crowdloan.rs +++ b/polkadot/runtime/common/src/crowdloan.rs @@ -74,8 +74,11 @@ use frame_support::{ }, }; use frame_system::ensure_signed; -use sp_runtime::{ModuleId, DispatchResult, - traits::{AccountIdConversion, Hash, Saturating, Zero, CheckedAdd, Bounded} +use sp_runtime::{ + ModuleId, DispatchResult, MultiSignature, MultiSigner, + traits::{ + AccountIdConversion, Hash, Saturating, Zero, CheckedAdd, Bounded, Verify, IdentifyAccount, + }, }; use crate::slots; use parity_scale_codec::{Encode, Decode}; @@ -140,6 +143,8 @@ pub struct FundInfo { parachain: Option, /// The owning account who placed the deposit. owner: AccountId, + /// An optional verifier. If exists, contributions must be signed by verifier. + verifier: Option, /// The amount of deposit placed. deposit: Balance, /// The total amount raised. @@ -256,6 +261,8 @@ decl_error! { HasActiveParachain, /// The retirement period has not ended. InRetirementPeriod, + /// Invalid signature. + InvalidSignature, } } @@ -273,7 +280,8 @@ decl_module! { #[compact] cap: BalanceOf, #[compact] first_slot: T::BlockNumber, #[compact] last_slot: T::BlockNumber, - #[compact] end: T::BlockNumber + #[compact] end: T::BlockNumber, + verifier: Option, ) { let owner = ensure_signed(origin)?; @@ -291,6 +299,7 @@ decl_module! { >::insert(index, FundInfo { parachain: None, owner, + verifier, deposit, raised: Zero::zero(), end, @@ -307,8 +316,14 @@ decl_module! { /// 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 is unable to be purchased and the timeout expires. + /// A valid signature maybe required in order to accept the contribution. #[weight = 0] - fn contribute(origin, #[compact] index: FundIndex, #[compact] value: BalanceOf) { + fn contribute( + origin, + #[compact] index: FundIndex, + #[compact] value: BalanceOf, + signature: Option + ) { let who = ensure_signed(origin)?; ensure!(value >= T::MinContribution::get(), Error::::ContributionTooSmall); @@ -320,10 +335,18 @@ decl_module! { let now = >::block_number(); ensure!(fund.end > now, Error::::ContributionPeriodOver); + let old_balance = Self::contribution_get(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); + } + T::Currency::transfer(&who, &Self::fund_account_id(index), value, AllowDeath)?; - let balance = Self::contribution_get(index, &who); - let balance = balance.saturating_add(value); + let balance = old_balance.saturating_add(value); Self::contribution_put(index, &who, &balance); if >::is_ending(now).is_some() { @@ -445,7 +468,7 @@ decl_module! { /// Withdraw full balance of a contributor to an unsuccessful or off-boarded fund. #[weight = 0] 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::::InvalidFundIndex)?; ensure!(fund.parachain.is_none(), Error::::FundNotRetired); @@ -576,11 +599,32 @@ impl Module { } } +#[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) -> 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)] mod tests { use super::*; - use std::{collections::HashMap, cell::RefCell}; + use std::{collections::HashMap, cell::RefCell, sync::Arc}; use frame_support::{ assert_ok, assert_noop, parameter_types, traits::{OnInitialize, OnFinalize}, @@ -593,6 +637,7 @@ mod tests { Permill, testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; + use sp_keystore::{KeystoreExt, testing::KeyStore}; use crate::slots::{self, Registrar}; use crate::crowdloan; @@ -769,7 +814,10 @@ mod tests { pallet_balances::GenesisConfig::{ balances: vec![(1, 1000), (2, 2000), (3, 3000), (4, 4000)], }.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) { @@ -805,12 +853,46 @@ mod tests { fn create_works() { new_test_ext().execute_with(|| { // 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); // This is what the initial `fund_info` should look like let fund_info = FundInfo { parachain: None, 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 = 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, raised: 0, // 5 blocks length + 3 block ending period + 1 starting block @@ -837,17 +919,17 @@ mod tests { new_test_ext().execute_with(|| { // Cannot create a crowdloan with bad slots assert_noop!( - Crowdloan::create(Origin::signed(1), 1000, 4, 1, 9), + Crowdloan::create(Origin::signed(1), 1000, 4, 1, 9, None), Error::::LastSlotBeforeFirstSlot ); assert_noop!( - Crowdloan::create(Origin::signed(1), 1000, 1, 5, 9), + Crowdloan::create(Origin::signed(1), 1000, 1, 5, 9, None), Error::::LastSlotTooFarInFuture ); // Cannot create a crowdloan without some deposit funds assert_noop!( - Crowdloan::create(Origin::signed(1337), 1000, 1, 3, 9), + Crowdloan::create(Origin::signed(1337), 1000, 1, 3, 9, None), BalancesError::::InsufficientBalance ); }); @@ -857,7 +939,7 @@ mod tests { fn contribute_works() { new_test_ext().execute_with(|| { // 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(Crowdloan::fund_account_id(0)), 1); @@ -865,7 +947,7 @@ mod tests { assert_eq!(Crowdloan::contribution_get(0, &1), 0); // 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 assert_eq!(Balances::free_balance(1), 950); // 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::::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::::InvalidSignature); + + // Valid signature wrong parameter + assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 50, Some(valid_signature.clone())), Error::::InvalidSignature); + assert_noop!(Crowdloan::contribute(Origin::signed(2), 0, 49, Some(valid_signature.clone())), Error::::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::::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] fn contribute_handles_basic_errors() { new_test_ext().execute_with(|| { // Cannot contribute to non-existing fund - assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49), Error::::InvalidFundIndex); + assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49, None), Error::::InvalidFundIndex); // Cannot contribute below minimum contribution - assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 9), Error::::ContributionTooSmall); + assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 9, None), Error::::ContributionTooSmall); // Set up a crowdloan - assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9)); - assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 101)); + assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 101, None)); // Cannot contribute past the limit - assert_noop!(Crowdloan::contribute(Origin::signed(2), 0, 900), Error::::CapExceeded); + assert_noop!(Crowdloan::contribute(Origin::signed(2), 0, 900, None), Error::::CapExceeded); // Move past end date run_to_block(10); // Cannot contribute to ended fund - assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49), Error::::ContributionPeriodOver); + assert_noop!(Crowdloan::contribute(Origin::signed(1), 0, 49, None), Error::::ContributionPeriodOver); }); } @@ -910,7 +1036,7 @@ mod tests { fn fix_deploy_data_works() { new_test_ext().execute_with(|| { // 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); // Add deploy data @@ -940,7 +1066,7 @@ mod tests { fn fix_deploy_data_handles_basic_errors() { new_test_ext().execute_with(|| { // 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); // Cannot set deploy data by non-owner @@ -988,7 +1114,7 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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); // Add deploy data @@ -1001,7 +1127,7 @@ mod tests { )); // 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); @@ -1024,11 +1150,11 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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); // 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); @@ -1062,7 +1188,7 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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); // Add deploy data @@ -1075,7 +1201,7 @@ mod tests { )); // 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); @@ -1105,7 +1231,7 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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); // Add deploy data @@ -1118,7 +1244,7 @@ mod tests { )); // 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); @@ -1150,11 +1276,11 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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 - assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100)); - assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200)); - assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300)); + assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, None)); // Skip all the way to the end run_to_block(50); @@ -1176,9 +1302,9 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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 - 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); run_to_block(5); @@ -1200,11 +1326,11 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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 - assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100)); - assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200)); - assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300)); + assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, None)); // Skip all the way to the end run_to_block(50); @@ -1238,12 +1364,12 @@ mod tests { ext.execute_with(|| { // Set up a crowdloan 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. for i in 0 .. 30 { 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); } @@ -1302,11 +1428,11 @@ mod tests { new_test_ext().execute_with(|| { // Set up a crowdloan 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 - assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100)); - assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200)); - assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300)); + assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, None)); // Cannot dissolve an invalid fund index assert_noop!(Crowdloan::dissolve(Origin::signed(1), 1), Error::::InvalidFundIndex); @@ -1334,9 +1460,9 @@ mod tests { fn fund_before_auction_works() { new_test_ext().execute_with(|| { // 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 - assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49)); + assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49, None)); // Fund added to NewRaise assert_eq!(Crowdloan::new_raise(), vec![0]); @@ -1376,12 +1502,12 @@ mod tests { assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); // Create two competing crowdloans, with end dates across multiple auctions // 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(2), 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, None)); // 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), 1, 200)); + assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 300, None)); + assert_ok!(Crowdloan::contribute(Origin::signed(1), 1, 200, None)); // Add deploy data to all assert_ok!(Crowdloan::fix_deploy_data( @@ -1413,7 +1539,7 @@ mod tests { // Create a second auction assert_ok!(Slots::new_auction(Origin::root(), 5, 1)); // 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! run_to_block(20); @@ -1464,14 +1590,22 @@ mod benchmarking { let caller = account("fund_creator", 0, 0); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - assert_ok!(Crowdloan::::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::::create(RawOrigin::Signed(caller).into(), cap, first_slot, last_slot, end, Some(pubkey))); FundCount::get() - 1 } fn contribute_fund(who: &T::AccountId, index: FundIndex) { T::Currency::make_free_balance_be(&who, BalanceOf::::max_value()); let value = T::MinContribution::get(); - assert_ok!(Crowdloan::::contribute(RawOrigin::Signed(who.clone()).into(), index, value)); + + let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec()); + let payload = (index, &who, BalanceOf::::default(), value); + let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey); + + assert_ok!(Crowdloan::::contribute(RawOrigin::Signed(who.clone()).into(), index, value, Some(sig))); } fn worst_validation_code() -> Vec { @@ -1540,9 +1674,10 @@ mod benchmarking { let end = T::BlockNumber::max_value(); let caller: T::AccountId = whitelisted_caller(); + let verifier = account("verifier", 0, 0); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: _(RawOrigin::Signed(caller), cap, first_slot, last_slot, end) + }: _(RawOrigin::Signed(caller), cap, first_slot, last_slot, end, Some(verifier)) verify { assert_last_event::(RawEvent::Created(FundCount::get() - 1).into()) } @@ -1553,7 +1688,12 @@ mod benchmarking { let caller: T::AccountId = whitelisted_caller(); let contribution = T::MinContribution::get(); T::Currency::make_free_balance_be(&caller, BalanceOf::::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::::default(), contribution); + let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey); + + }: _(RawOrigin::Signed(caller.clone()), fund_index, contribution, Some(sig)) verify { // NewRaise is appended to, so we don't need to fill it up for worst case scenario. assert!(!NewRaise::get().is_empty()); @@ -1638,12 +1778,18 @@ mod benchmarking { let n in 2 .. 100; let end_block: T::BlockNumber = 100u32.into(); + let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec()); + for i in 0 .. n { let fund_index = create_fund::(end_block); let contributor: T::AccountId = account("contributor", i, 0); let contribution = T::MinContribution::get() * (i + 1).into(); T::Currency::make_free_balance_be(&contributor, BalanceOf::::max_value()); - Crowdloan::::contribute(RawOrigin::Signed(contributor).into(), fund_index, contribution)?; + + let payload = (fund_index, &contributor, BalanceOf::::default(), contribution); + let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey.clone()); + + Crowdloan::::contribute(RawOrigin::Signed(contributor).into(), fund_index, contribution, Some(sig))?; } let lease_period_index = end_block / T::LeasePeriod::get();