Newer
Older
System::set_block_number(System::block_number() + 1);
System::on_initialize(System::block_number());
Balances::on_initialize(System::block_number());
Crowdloan::on_initialize(System::block_number());
fn last_event() -> Event {
System::events().pop().expect("Event expected").event
}
new_test_ext().execute_with(|| {
assert_eq!(System::block_number(), 0);
assert_eq!(Crowdloan::funds(ParaId::from(0)), None);
let empty: Vec<ParaId> = Vec::new();
assert_eq!(Crowdloan::contribution_get(0u32, &1).0, 0);
assert_ok!(TestAuctioneer::new_auction(5, 0));
assert_eq!(bids(), vec![]);
assert_ok!(TestAuctioneer::place_bid(1, 2.into(), 0, 3, 6));
let b = BidPlaced { height: 0, bidder: 1, para: 2.into(), first_period: 0, last_period: 3, amount: 6 };
assert_eq!(TestAuctioneer::auction_status(4), AuctionStatus::<u64>::StartingPeriod);
assert_eq!(TestAuctioneer::auction_status(5), AuctionStatus::<u64>::EndingPeriod(0, 0));
assert_eq!(TestAuctioneer::auction_status(9), AuctionStatus::<u64>::EndingPeriod(4, 0));
assert_eq!(TestAuctioneer::auction_status(11), AuctionStatus::<u64>::NotStarted);
});
}
#[test]
fn create_works() {
new_test_ext().execute_with(|| {
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, None));
// This is what the initial `fund_info` should look like
let fund_info = FundInfo {
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_period: 1,
last_period: 4,
assert_eq!(Crowdloan::funds(para), Some(fund_info));
// User has deposit removed from their free balance
assert_eq!(Balances::free_balance(1), 999);
// Deposit is placed in reserved
assert_eq!(Balances::reserved_balance(1), 1);
// No new raise until first contribution
let empty: Vec<ParaId> = 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), para, 1000, 1, 4, 9, Some(pubkey.clone())));
// This is what the initial `fund_info` should look like
let fund_info = FundInfo {
deposit: 1,
raised: 0,
// 5 blocks length + 3 block ending period + 1 starting block
end: 9,
cap: 1000,
last_contribution: LastContribution::Never,
first_period: 1,
last_period: 4,
assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info));
// User has deposit removed from their free balance
assert_eq!(Balances::free_balance(1), 999);
// Deposit is placed in reserved
assert_eq!(Balances::reserved_balance(1), 1);
// No new raise until first contribution
let empty: Vec<ParaId> = Vec::new();
});
}
#[test]
fn create_handles_basic_errors() {
new_test_ext().execute_with(|| {
// Now try to create a crowdloan campaign
let para = new_para();
let e = Error::<Test>::InvalidParaId;
assert_noop!(Crowdloan::create(Origin::signed(1), 1.into(), 1000, 1, 4, 9, None), e);
// Cannot create a crowdloan with bad lease periods
let e = Error::<Test>::LastPeriodBeforeFirstPeriod;
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 4, 1, 9, None), e);
let e = Error::<Test>::LastPeriodTooFarInFuture;
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 9, 9, None), e);
// Cannot create a crowdloan without some deposit funds
assert_ok!(TestRegistrar::<Test>::register(1337, ParaId::from(1234), Default::default(), Default::default()));
let e = BalancesError::<Test, _>::InsufficientBalance;
assert_noop!(Crowdloan::create(Origin::signed(1337), ParaId::from(1234), 1000, 1, 3, 9, None), e);
// Cannot create a crowdloan with nonsense end date
// This crowdloan would end in lease period 2, but is bidding for some slot that starts in lease period 1.
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 41, None), Error::<Test>::EndTooFarInFuture);
});
}
#[test]
fn contribute_works() {
new_test_ext().execute_with(|| {
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, None));
assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 0);
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 49, None));
// User 1 has spent some funds to do this, transfer fees **are** taken
// Contributions are stored in the trie
assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 49);
// Contributions appear in free balance of crowdloan
assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(para)), 49);
assert_eq!(Crowdloan::new_raise(), vec![para]);
let fund = Crowdloan::funds(para).unwrap();
// Last contribution time recorded
assert_eq!(fund.last_contribution, LastContribution::PreEnding(0));
assert_eq!(fund.raised, 49);
});
}
#[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), para, 1000, 1, 4, 9, Some(pubkey.clone())));
// No contributions yet
assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 0);
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 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), para, 49, Some(invalid_signature)), Error::<Test>::InvalidSignature);
// Valid signature wrong parameter
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 50, Some(valid_signature.clone())), Error::<Test>::InvalidSignature);
assert_noop!(Crowdloan::contribute(Origin::signed(2), para, 49, Some(valid_signature.clone())), Error::<Test>::InvalidSignature);
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 49, Some(valid_signature.clone())));
// Reuse valid signature
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 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), para, 10, Some(valid_signature_2)));
// Contributions appear in free balance of crowdloan
assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(para)), 59);
// Contribution amount is correct
let fund = Crowdloan::funds(para).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), para, 49, None), Error::<Test>::InvalidParaId);
// Cannot contribute below minimum contribution
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 9, None), Error::<Test>::ContributionTooSmall);
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 101, None));
// Cannot contribute past the limit
assert_noop!(Crowdloan::contribute(Origin::signed(2), para, 900, None), Error::<Test>::CapExceeded);
// Move past end date
run_to_block(10);
// Cannot contribute to ended fund
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, None), Error::<Test>::ContributionPeriodOver);
// If a crowdloan has already won, it should not allow contributions.
let para_2 = new_para();
assert_ok!(Crowdloan::create(Origin::signed(1), para_2, 1000, 1, 4, 40, None));
// Emulate a win by leasing out and putting a deposit. Slots pallet would normally do this.
let crowdloan_account = Crowdloan::fund_account_id(para_2);
set_winner(para_2, crowdloan_account, true);
assert_noop!(Crowdloan::contribute(Origin::signed(1), para_2, 49, None), Error::<Test>::BidOrLeaseActive);
// Move past lease period 1, should not be allowed to have further contributions with a crowdloan
// that has starting period 1.
let para_3 = new_para();
assert_ok!(Crowdloan::create(Origin::signed(1), para_3, 1000, 1, 4, 40, None));
run_to_block(40);
assert_eq!(TestAuctioneer::lease_period_index(), 2);
assert_noop!(Crowdloan::contribute(Origin::signed(1), para_3, 49, None), Error::<Test>::ContributionPeriodOver);
fn cannot_contribute_during_vrf() {
new_test_ext().execute_with(|| {
set_vrf_delay(5);
let first_period = 1;
let last_period = 4;
assert_ok!(TestAuctioneer::new_auction(5, 0));
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, first_period, last_period, 20, None));
run_to_block(8);
// Can def contribute when auction is running.
assert!(TestAuctioneer::auction_status(System::block_number()).is_ending().is_some());
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 250, None));
run_to_block(10);
// Can't contribute when auction is in the VRF delay period.
assert!(TestAuctioneer::auction_status(System::block_number()).is_vrf());
assert_noop!(Crowdloan::contribute(Origin::signed(2), para, 250, None), Error::<Test>::VrfDelayInProgress);
run_to_block(15);
// Its fine to contribute when no auction is running.
assert!(!TestAuctioneer::auction_status(System::block_number()).is_in_progress());
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 250, None));
})
}
#[test]
fn bidding_works() {
new_test_ext().execute_with(|| {
let para = new_para();
let first_period = 1;
let last_period = 4;
assert_ok!(TestAuctioneer::new_auction(5, 0));
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, first_period, last_period, 9, None));
let bidder = Crowdloan::fund_account_id(para);
run_to_block(1);
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
run_to_block(3);
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 150, None));
run_to_block(5);
assert_ok!(Crowdloan::contribute(Origin::signed(4), para, 200, None));
run_to_block(8);
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 250, None));
BidPlaced { height: 5, amount: 250, bidder, para, first_period, last_period },
BidPlaced { height: 6, amount: 450, bidder, para, first_period, last_period },
BidPlaced { height: 9, amount: 700, bidder, para, first_period, last_period },
new_test_ext().execute_with(|| {
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
let account_id = Crowdloan::fund_account_id(para);
// para has no reserved funds, indicating it did not win the auction.
assert_eq!(Balances::reserved_balance(&account_id), 0);
// but there's still the funds in its balance.
assert_eq!(Balances::free_balance(&account_id), 150);
assert_eq!(Balances::free_balance(2), 1900);
assert_eq!(Balances::free_balance(3), 2950);
assert_ok!(Crowdloan::withdraw(Origin::signed(2), 2, para));
assert_eq!(Balances::free_balance(&account_id), 50);
assert_eq!(Balances::free_balance(2), 2000);
assert_ok!(Crowdloan::withdraw(Origin::signed(2), 3, para));
assert_eq!(Balances::free_balance(&account_id), 0);
assert_eq!(Balances::free_balance(3), 3000);
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
#[test]
fn withdraw_cannot_be_griefed() {
new_test_ext().execute_with(|| {
let para = new_para();
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
run_to_block(10);
let account_id = Crowdloan::fund_account_id(para);
// user sends the crowdloan funds trying to make an accounting error
assert_ok!(Balances::transfer(Origin::signed(1), account_id, 10));
// overfunded now
assert_eq!(Balances::free_balance(&account_id), 110);
assert_eq!(Balances::free_balance(2), 1900);
assert_ok!(Crowdloan::withdraw(Origin::signed(2), 2, para));
assert_eq!(Balances::free_balance(2), 2000);
// Some funds are left over
assert_eq!(Balances::free_balance(&account_id), 10);
// They wil be left in the account at the end
assert_ok!(Crowdloan::dissolve(Origin::signed(1), para));
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
assert_eq!(Balances::free_balance(&account_id), 10);
});
}
#[test]
fn refund_works() {
new_test_ext().execute_with(|| {
let para = new_para();
let account_id = Crowdloan::fund_account_id(para);
// Set up a crowdloan ending on 9
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
// Make some contributions
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 200, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 300, None));
assert_eq!(Balances::free_balance(account_id), 600);
// Can't refund before the crowdloan it has ended
assert_noop!(
Crowdloan::refund(Origin::signed(1337), para),
Error::<Test>::FundNotEnded,
);
// Move to the end of the crowdloan
run_to_block(10);
assert_ok!(Crowdloan::refund(Origin::signed(1337), para));
// Funds are returned
assert_eq!(Balances::free_balance(account_id), 0);
// 1 deposit for the crowdloan which hasn't dissolved yet.
assert_eq!(Balances::free_balance(1), 1000 - 1);
assert_eq!(Balances::free_balance(2), 2000);
assert_eq!(Balances::free_balance(3), 3000);
fn multiple_refund_works() {
new_test_ext().execute_with(|| {
let account_id = Crowdloan::fund_account_id(para);
// Set up a crowdloan ending on 9
assert_ok!(Crowdloan::create(Origin::signed(1), para, 100000, 1, 1, 9, None));
// Make more contributions than our limit
for i in 1 ..= RemoveKeysLimit::get() * 2 {
Balances::make_free_balance_be(&i.into(), (1000 * i).into());
assert_ok!(Crowdloan::contribute(Origin::signed(i.into()), para, (i * 100).into(), None));
}
assert_eq!(Balances::free_balance(account_id), 21000);
// Move to the end of the crowdloan
run_to_block(10);
assert_ok!(Crowdloan::refund(Origin::signed(1337), para));
assert_eq!(last_event(), super::Event::<Test>::PartiallyRefunded(para).into());
// Funds still left over
assert!(!Balances::free_balance(account_id).is_zero());
// Call again
assert_ok!(Crowdloan::refund(Origin::signed(1337), para));
assert_eq!(last_event(), super::Event::<Test>::AllRefunded(para).into());
// Funds are returned
assert_eq!(Balances::free_balance(account_id), 0);
// 1 deposit for the crowdloan which hasn't dissolved yet.
for i in 1 ..= RemoveKeysLimit::get() * 2 {
assert_eq!(Balances::free_balance(&i.into()), i as u64 * 1000);
}
});
}
#[test]
fn refund_and_dissolve_works() {
new_test_ext().execute_with(|| {
let para = new_para();
let issuance = Balances::total_issuance();
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
// All funds are refunded
assert_ok!(Crowdloan::refund(Origin::signed(2), para));
// Now that `fund.raised` is zero, it can be dissolved.
assert_ok!(Crowdloan::dissolve(Origin::signed(1), para));
assert_eq!(Balances::free_balance(1), 1000);
assert_eq!(Balances::free_balance(2), 2000);
assert_eq!(Balances::free_balance(3), 3000);
assert_eq!(Balances::total_issuance(), issuance);
fn dissolve_works() {
new_test_ext().execute_with(|| {
let para = new_para();
let issuance = Balances::total_issuance();
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
// Can't dissolve before it ends
assert_noop!(Crowdloan::dissolve(Origin::signed(1), para), Error::<Test>::NotReadyToDissolve);
set_winner(para, 1, true);
// Can't dissolve when it won.
assert_noop!(Crowdloan::dissolve(Origin::signed(1), para), Error::<Test>::NotReadyToDissolve);
set_winner(para, 1, false);
// Can't dissolve while it still has user funds
assert_noop!(Crowdloan::dissolve(Origin::signed(1), para), Error::<Test>::NotReadyToDissolve);
// All funds are refunded
assert_ok!(Crowdloan::refund(Origin::signed(2), para));
// Now that `fund.raised` is zero, it can be dissolved.
assert_ok!(Crowdloan::dissolve(Origin::signed(1), para));
assert_eq!(Balances::free_balance(1), 1000);
assert_eq!(Balances::free_balance(2), 2000);
assert_eq!(Balances::free_balance(3), 3000);
assert_eq!(Balances::total_issuance(), issuance);
fn withdraw_from_finished_works() {
new_test_ext().execute_with(|| {
let para = new_para();
let account_id = Crowdloan::fund_account_id(para);
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
// Fund crowdloans.
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
// simulate the reserving of para's funds. this actually happens in the Slots pallet.
assert_ok!(Balances::reserve(&account_id, 150));
run_to_block(19);
assert_noop!(Crowdloan::withdraw(Origin::signed(2), 2, para), Error::<Test>::BidOrLeaseActive);
run_to_block(20);
// simulate the unreserving of para's funds, now that the lease expired. this actually
// happens in the Slots pallet.
Balances::unreserve(&account_id, 150);
// para has no reserved funds, indicating it did ot win the auction.
assert_eq!(Balances::reserved_balance(&account_id), 0);
// but there's still the funds in its balance.
assert_eq!(Balances::free_balance(&account_id), 150);
assert_eq!(Balances::free_balance(2), 1900);
assert_eq!(Balances::free_balance(3), 2950);
assert_ok!(Crowdloan::withdraw(Origin::signed(2), 2, para));
assert_eq!(Balances::free_balance(&account_id), 50);
assert_ok!(Crowdloan::withdraw(Origin::signed(2), 3, para));
assert_eq!(Balances::free_balance(&account_id), 0);
new_test_ext().execute_with(|| {
let para_1 = new_para();
let para_2 = new_para();
// Set up crowdloans
assert_ok!(Crowdloan::create(Origin::signed(1), para_1, 1000, 1, 1, 9, None));
assert_ok!(Crowdloan::create(Origin::signed(1), para_2, 1000, 1, 1, 9, None));
// Different contributions
assert_ok!(Crowdloan::contribute(Origin::signed(2), para_1, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para_2, 50, None));
// Original state
assert_eq!(Funds::<Test>::get(para_1).unwrap().raised, 100);
assert_eq!(Funds::<Test>::get(para_2).unwrap().raised, 50);
// Swap
Crowdloan::on_swap(para_1, para_2);
// Final state
assert_eq!(Funds::<Test>::get(para_2).unwrap().raised, 100);
assert_eq!(Funds::<Test>::get(para_1).unwrap().raised, 50);
fn cannot_create_fund_when_already_active() {
new_test_ext().execute_with(|| {
assert_ok!(Crowdloan::create(Origin::signed(1), para_1, 1000, 1, 1, 9, None));
// Cannot create a fund again
assert_noop!(
Crowdloan::create(Origin::signed(1), para_1, 1000, 1, 1, 9, None),
Error::<Test>::FundNotEnded,
);
#[test]
fn edit_works() {
new_test_ext().execute_with(|| {
let para_1 = new_para();
assert_ok!(Crowdloan::create(Origin::signed(1), para_1, 1000, 1, 1, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), para_1, 100, None));
let old_crowdloan = Crowdloan::funds(para_1).unwrap();
assert_ok!(Crowdloan::edit(Origin::root(), para_1, 1234, 2, 3, 4, None));
let new_crowdloan = Crowdloan::funds(para_1).unwrap();
// Some things stay the same
assert_eq!(old_crowdloan.depositor, new_crowdloan.depositor);
assert_eq!(old_crowdloan.deposit, new_crowdloan.deposit);
assert_eq!(old_crowdloan.raised, new_crowdloan.raised);
// Some things change
assert!(old_crowdloan.cap != new_crowdloan.cap);
assert!(old_crowdloan.first_period != new_crowdloan.first_period);
assert!(old_crowdloan.last_period != new_crowdloan.last_period);
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
#[test]
fn add_memo_works() {
new_test_ext().execute_with(|| {
let para_1 = new_para();
assert_ok!(Crowdloan::create(Origin::signed(1), para_1, 1000, 1, 1, 9, None));
// Cant add a memo before you have contributed.
assert_noop!(
Crowdloan::add_memo(Origin::signed(1), para_1, b"hello, world".to_vec()),
Error::<Test>::NoContributions,
);
// Make a contribution. Initially no memo.
assert_ok!(Crowdloan::contribute(Origin::signed(1), para_1, 100, None));
assert_eq!(Crowdloan::contribution_get(0u32, &1), (100, vec![]));
// Can't place a memo that is too large.
assert_noop!(
Crowdloan::add_memo(Origin::signed(1), para_1, vec![123; 123]),
Error::<Test>::MemoTooLarge,
);
// Adding a memo to an existing contribution works
assert_ok!(Crowdloan::add_memo(Origin::signed(1), para_1, b"hello, world".to_vec()));
assert_eq!(Crowdloan::contribution_get(0u32, &1), (100, b"hello, world".to_vec()));
// Can contribute again and data persists
assert_ok!(Crowdloan::contribute(Origin::signed(1), para_1, 100, None));
assert_eq!(Crowdloan::contribution_get(0u32, &1), (200, b"hello, world".to_vec()));
});
}
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
#[test]
fn poke_works() {
new_test_ext().execute_with(|| {
let para_1 = new_para();
assert_ok!(TestAuctioneer::new_auction(5, 0));
assert_ok!(Crowdloan::create(Origin::signed(1), para_1, 1000, 1, 1, 9, None));
// Should fail when no contributions.
assert_noop!(
Crowdloan::poke(Origin::signed(1), para_1),
Error::<Test>::NoContributions
);
assert_ok!(Crowdloan::contribute(Origin::signed(2), para_1, 100, None));
run_to_block(6);
assert_ok!(Crowdloan::poke(Origin::signed(1), para_1));
assert_eq!(Crowdloan::new_raise(), vec![para_1]);
assert_noop!(
Crowdloan::poke(Origin::signed(1), para_1),
Error::<Test>::AlreadyInNewRaise
);
});
}
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking {
use super::{*, Pallet as Crowdloan};
use frame_system::RawOrigin;
use frame_support::{
assert_ok,
traits::OnInitialize,
};
use sp_runtime::traits::{Bounded, CheckedSub};
use frame_benchmarking::{benchmarks, whitelisted_caller, account, impl_benchmark_test_suite};
fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) {
Shaun Wang
committed
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::Event = generic_event.into();
// compare to the last event record
let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}
fn create_fund<T: Config>(id: u32, end: T::BlockNumber) -> ParaId {
let lease_period_index = T::Auctioneer::lease_period_index();
let first_period = lease_period_index;
let last_period = lease_period_index + ((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into();
let caller = account("fund_creator", id, 0);
CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Assume ed25519 is most complex signature format
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
let head_data = T::Registrar::worst_head_data();
let validation_code = T::Registrar::worst_validation_code();
assert_ok!(T::Registrar::register(caller.clone(), para_id, head_data, validation_code));
T::Registrar::execute_pending_transitions();
assert_ok!(Crowdloan::<T>::create(
RawOrigin::Signed(caller).into(),
para_id,
cap,
first_period,
last_period,
end,
Some(pubkey)
));
para_id
fn contribute_fund<T: Config>(who: &T::AccountId, index: ParaId) {
CurrencyOf::<T>::make_free_balance_be(&who, BalanceOf::<T>::max_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)));
let first_period = 0u32.into();
let last_period = 3u32.into();
let end = T::Auctioneer::lease_period();
let caller: T::AccountId = whitelisted_caller();
let head_data = T::Registrar::worst_head_data();
let validation_code = T::Registrar::worst_validation_code();
let verifier = account("verifier", 0, 0);
CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Registrar::register(caller.clone(), para_id, head_data, validation_code)?;
T::Registrar::execute_pending_transitions();
}: _(RawOrigin::Signed(caller), para_id, cap, first_period, last_period, end, Some(verifier))
assert_last_event::<T>(Event::<T>::Created(para_id).into())
}
// Contribute has two arms: PreEnding and Ending, but both are equal complexity.
contribute {
let fund_index = create_fund::<T>(1, 100u32.into());
let caller: T::AccountId = whitelisted_caller();
let contribution = T::MinContribution::get();
CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
assert!(NewRaise::<T>::get().is_empty());
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 {
// NewRaise is appended to, so we don't need to fill it up for worst case scenario.
assert!(!NewRaise::<T>::get().is_empty());
assert_last_event::<T>(Event::<T>::Contributed(caller, fund_index, contribution).into());
let fund_index = create_fund::<T>(1337, 100u32.into());
let caller: T::AccountId = whitelisted_caller();
let contributor = account("contributor", 0, 0);
contribute_fund::<T>(&contributor, fund_index);
Shaun Wang
committed
frame_system::Pallet::<T>::set_block_number(200u32.into());
}: _(RawOrigin::Signed(caller), contributor.clone(), fund_index)
verify {
assert_last_event::<T>(Event::<T>::Withdrew(contributor, fund_index, T::MinContribution::get()).into());
// Worst case: Refund removes `RemoveKeysLimit` keys, and is fully refunded.
refund {
let k in 0 .. T::RemoveKeysLimit::get();
let fund_index = create_fund::<T>(1337, 100u32.into());
// Dissolve will remove at most `RemoveKeysLimit` at once.
contribute_fund::<T>(&account("contributor", i, 0), fund_index);
}
let caller: T::AccountId = whitelisted_caller();
frame_system::Pallet::<T>::set_block_number(200u32.into());
}: _(RawOrigin::Signed(caller), fund_index)
verify {
assert_last_event::<T>(Event::<T>::AllRefunded(fund_index).into());
dissolve {
let fund_index = create_fund::<T>(1337, 100u32.into());
let caller: T::AccountId = whitelisted_caller();
Shaun Wang
committed
frame_system::Pallet::<T>::set_block_number(T::BlockNumber::max_value());
}: _(RawOrigin::Signed(caller.clone()), fund_index)
verify {
assert_last_event::<T>(Event::<T>::Dissolved(fund_index).into());
edit {
let para_id = ParaId::from(1);
let cap = BalanceOf::<T>::max_value();
let first_period = 0u32.into();
let last_period = 3u32.into();
let end = T::Auctioneer::lease_period();
let caller: T::AccountId = whitelisted_caller();
let head_data = T::Registrar::worst_head_data();
let validation_code = T::Registrar::worst_validation_code();
let verifier: MultiSigner = account("verifier", 0, 0);
CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Registrar::register(caller.clone(), para_id, head_data, validation_code)?;
T::Registrar::execute_pending_transitions();
Crowdloan::<T>::create(
RawOrigin::Signed(caller).into(),
para_id, cap, first_period, last_period, end, Some(verifier.clone()),
)?;
// Doesn't matter what we edit to, so use the same values.
}: _(RawOrigin::Root, para_id, cap, first_period, last_period, end, Some(verifier))
assert_last_event::<T>(Event::<T>::Edited(para_id).into())
add_memo {
let fund_index = create_fund::<T>(1, 100u32.into());
let caller: T::AccountId = whitelisted_caller();
contribute_fund::<T>(&caller, fund_index);
let worst_memo = vec![42; T::MaxMemoLength::get().into()];
}: _(RawOrigin::Signed(caller.clone()), fund_index, worst_memo.clone())
verify {
let fund = Funds::<T>::get(fund_index).expect("fund was created...");
assert_eq!(
Crowdloan::<T>::contribution_get(fund.trie_index, &caller),
(T::MinContribution::get(), worst_memo),
);
}
poke {
let fund_index = create_fund::<T>(1, 100u32.into());
let caller: T::AccountId = whitelisted_caller();
contribute_fund::<T>(&caller, fund_index);
NewRaise::<T>::kill();
assert!(NewRaise::<T>::get().is_empty());
}: _(RawOrigin::Signed(caller), fund_index)
verify {
assert!(!NewRaise::<T>::get().is_empty());
assert_last_event::<T>(Event::<T>::AddedToNewRaise(fund_index).into())
// Worst case scenario: N funds are all in the `NewRaise` list, we are
// in the beginning of the ending period, and each fund outbids the next
on_initialize {
// We test the complexity over different number of new raise
let n in 2 .. 100;
let end_block: T::BlockNumber = 100u32.into();
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
let fund_index = create_fund::<T>(i, end_block);
let contributor: T::AccountId = account("contributor", i, 0);
let contribution = T::MinContribution::get() * (i + 1).into();
let payload = (fund_index, &contributor, BalanceOf::<T>::default(), contribution);
let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey.clone());
CurrencyOf::<T>::make_free_balance_be(&contributor, BalanceOf::<T>::max_value());
Crowdloan::<T>::contribute(RawOrigin::Signed(contributor).into(), fund_index, contribution, Some(sig))?;
let lease_period_index = T::Auctioneer::lease_period_index();
let duration = end_block
Shaun Wang
committed
.checked_sub(&frame_system::Pallet::<T>::block_number())
.ok_or("duration of auction less than zero")?;
T::Auctioneer::new_auction(duration, lease_period_index)?;
assert_eq!(T::Auctioneer::auction_status(end_block).is_ending(), Some((0u32.into(), 0u32.into())));
assert_eq!(NewRaise::<T>::get().len(), n as usize);
let old_endings_count = EndingsCount::<T>::get();
}: {
Crowdloan::<T>::on_initialize(end_block);
} verify {
assert_eq!(EndingsCount::<T>::get(), old_endings_count + 1);
assert_last_event::<T>(Event::<T>::HandleBidResult((n - 1).into(), Ok(())).into());
impl_benchmark_test_suite!(
Crowdloan,
crate::integration_tests::new_test_ext(),
crate::integration_tests::Test,
);