From d993fcc7837e8e2b300b30cbe78495e35432ff8a Mon Sep 17 00:00:00 2001 From: gatsey <42411328+f-gate@users.noreply.github.com> Date: Sat, 9 Dec 2023 09:59:11 +0000 Subject: [PATCH] Release V1 (#284) * staging check for prs to main * remove run gcp for push to staging * Optional fellowship switch (#280) * add test for approved applicant * fmt * add ensure role to runtime --------- Co-authored-by: Sam Elamin * Foreign token minting tests (#282) * test * forign signer can mint * negative minting test * remove it (#285) * Autofinalise dispute where jury size == 1 (#279) * autocomplete dispute where jury size = 1 * refactor tests to include jury * fmt * give briefs a jury! * fmt * fix tests from merge * remove it (#285) --------- Co-authored-by: Sam Elamin * clear votes with tests (#289) * Eoa field (#287) * new types * ensure supported currency method * brief migration, fixes * fixes * fmt * fix up benchmarks * fix brief test * update imbue image for rococo * clear votes with tests * fix merge * fix * fix * fmt * fix --------- Co-authored-by: samelamin * bump up the version to match polkadot packages --------- Co-authored-by: Sam Elamin --- .github/workflows/build.yml | 2 - libs/common-types/src/tokens.rs | 5 +- pallets/briefs/src/benchmarking.rs | 13 +- pallets/briefs/src/integration_tests.rs | 1 + pallets/briefs/src/lib.rs | 9 + pallets/briefs/src/migrations.rs | 1 - pallets/briefs/src/mock.rs | 36 +++- pallets/briefs/src/tests.rs | 55 ++++- pallets/disputes/src/impls.rs | 2 +- pallets/fellowship/src/impls.rs | 2 +- pallets/fellowship/src/traits.rs | 4 +- pallets/proposals/src/benchmarking.rs | 39 +++- pallets/proposals/src/impls/pallet_impls.rs | 6 + pallets/proposals/src/lib.rs | 22 +- pallets/proposals/src/migration.rs | 12 +- pallets/proposals/src/mock.rs | 3 + pallets/proposals/src/test_utils.rs | 3 +- pallets/proposals/src/tests/disputes.rs | 132 ++++++++++-- pallets/proposals/src/tests/foreign_asset.rs | 80 ++++++++ pallets/proposals/src/tests/mod.rs | 1 + pallets/proposals/src/tests/pallet.rs | 201 ++++++++++++++++--- pallets/proposals/src/tests/refunds.rs | 44 +++- runtime/imbue-kusama/src/lib.rs | 3 +- 23 files changed, 593 insertions(+), 83 deletions(-) create mode 100644 pallets/proposals/src/tests/foreign_asset.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58b35af0..bc29d304 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,6 @@ on: - "**.md" env: CARGO_TERM_COLOR: always - GCP_ZONE: europe-west3-a jobs: check_branch: @@ -41,7 +40,6 @@ jobs: image_family: ubuntu-2004-lts machine_type: e2-highcpu-32 disk_size: 100 - machine_zone: ${{ env.GCP_ZONE }} ephemeral: true test-features: diff --git a/libs/common-types/src/tokens.rs b/libs/common-types/src/tokens.rs index bd74a9eb..d045a037 100644 --- a/libs/common-types/src/tokens.rs +++ b/libs/common-types/src/tokens.rs @@ -97,7 +97,10 @@ impl ForeignOwnedAccount { } #[cfg(feature = "runtime-benchmarks")] pub fn get_supported_currency_eoa_combo() -> (ForeignOwnedAccount, CurrencyId) { - (ForeignOwnedAccount::ETH(Default::default()), CurrencyId::ForeignAsset(ForeignAssetId::ETH)) + ( + ForeignOwnedAccount::ETH(Default::default()), + CurrencyId::ForeignAsset(ForeignAssetId::ETH), + ) } } diff --git a/pallets/briefs/src/benchmarking.rs b/pallets/briefs/src/benchmarking.rs index f3052d39..88e83fa5 100644 --- a/pallets/briefs/src/benchmarking.rs +++ b/pallets/briefs/src/benchmarking.rs @@ -42,7 +42,8 @@ mod benchmarks { brief_id, currency_id, milestones, - Some(eoa) + Some(eoa), + false, ); assert_last_event::(Event::::BriefSubmitted(caller, brief_id).into()); } @@ -68,6 +69,7 @@ mod benchmarks { currency_id, milestones, None, + false, )); let brief_owner: T::AccountId = brief_owners[0].clone(); // (brief_owner, brief_id, contribution) @@ -91,7 +93,6 @@ mod benchmarks { let brief_id = gen_hash(1); let milestones = get_max_milestones::(); - assert_ok!(Briefs::::create_brief( RawOrigin::Signed(caller).into(), brief_owners, @@ -102,6 +103,7 @@ mod benchmarks { currency_id, milestones, None, + false, )); // (origin, brief_id) #[extrinsic_call] @@ -129,6 +131,7 @@ mod benchmarks { currency_id, milestones, None, + false, )); // (origin, brief_id) #[extrinsic_call] @@ -143,7 +146,11 @@ mod benchmarks { ); } -fn create_account_id(suri: &'static str, n: u32, currency_id: CurrencyId) -> T::AccountId { +fn create_account_id( + suri: &'static str, + n: u32, + currency_id: CurrencyId, +) -> T::AccountId { let user = account(suri, n, SEED); let initial_balance = 1_000_000_000_000_000u128; assert_ok!(T::RMultiCurrency::deposit( diff --git a/pallets/briefs/src/integration_tests.rs b/pallets/briefs/src/integration_tests.rs index c43eb534..5edcd1f2 100644 --- a/pallets/briefs/src/integration_tests.rs +++ b/pallets/briefs/src/integration_tests.rs @@ -23,6 +23,7 @@ fn create_proposal_from_brief() { CurrencyId::Native, get_milestones(10), None, + false, ); assert_ok!(BriefsMod::commence_work( diff --git a/pallets/briefs/src/lib.rs b/pallets/briefs/src/lib.rs index 6748e243..fe6892a8 100644 --- a/pallets/briefs/src/lib.rs +++ b/pallets/briefs/src/lib.rs @@ -33,6 +33,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use pallet_deposits::traits::DepositHandler; + use pallet_fellowship::traits::EnsureRole; use pallet_fellowship::traits::SelectJury; use pallet_proposals::traits::IntoProposal; use pallet_proposals::{Contribution, FundingPath, ProposedMilestone}; @@ -84,9 +85,12 @@ pub mod pallet { type MaxMilestonesPerBrief: Get; /// Storage deposits. type BriefStorageItem: Get>; + /// Handler for deposits. type DepositHandler: DepositHandler, AccountIdOf>; /// The type that selects a list of jury members. type JurySelector: SelectJury>; + /// Type for ensuring an account is of a given fellowship role. + type EnsureRole: pallet_fellowship::traits::EnsureRole>; /// The weight info for the extrinsics. type WeightInfo: WeightInfoT; } @@ -177,9 +181,14 @@ pub mod pallet { currency_id: CurrencyId, milestones: BoundedProposedMilestones, external_owned_address: Option, + require_fellowship: bool, ) -> DispatchResult { let who = ensure_signed(origin)?; + if require_fellowship { + T::EnsureRole::ensure_role(&applicant, pallet_fellowship::Role::Freelancer, None)?; + } + ensure!( Briefs::::get(brief_id).is_none(), Error::::BriefAlreadyExists diff --git a/pallets/briefs/src/migrations.rs b/pallets/briefs/src/migrations.rs index 8c527918..6f941a3c 100644 --- a/pallets/briefs/src/migrations.rs +++ b/pallets/briefs/src/migrations.rs @@ -82,7 +82,6 @@ pub(crate) mod v1 { }; v2::BriefsV2::::insert(key, migrated); - } }) } diff --git a/pallets/briefs/src/mock.rs b/pallets/briefs/src/mock.rs index 2d67a24b..de4b9ec1 100644 --- a/pallets/briefs/src/mock.rs +++ b/pallets/briefs/src/mock.rs @@ -18,6 +18,8 @@ use sp_runtime::{ }; use pallet_deposits::traits::DepositHandler; +use pallet_fellowship::traits::FellowshipHandle; +use pallet_fellowship::Role; use sp_std::{ convert::{TryFrom, TryInto}, str, @@ -57,6 +59,7 @@ frame_support::construct_runtime!( BriefsMod: pallet_briefs::{Pallet, Call, Storage, Event}, Proposals: pallet_proposals::{Pallet, Call, Storage, Event}, Identity: pallet_identity::{Pallet, Call, Storage, Event}, + Fellowship: pallet_fellowship::{Pallet, Call, Storage, Event}, } ); @@ -209,6 +212,7 @@ impl pallet_briefs::Config for Test { type DepositHandler = MockDepositHandler; type WeightInfo = pallet_briefs::WeightInfo; type JurySelector = MockJurySelector; + type EnsureRole = pallet_fellowship::impls::EnsureFellowshipRole; } parameter_types! { @@ -273,14 +277,39 @@ impl pallet_identity::Config for Test { type WeightInfo = (); } +parameter_types! { + pub MaxCandidatesPerShortlist: u32 = 100; + pub ShortlistPeriod: BlockNumber = 100; + pub MembershipDeposit: Balance = 50_000_000; + pub SlashAccount: AccountId = 1; + pub DepositCurrencyId: CurrencyId = CurrencyId::Native; +} + +impl pallet_fellowship::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MultiCurrency = Tokens; + type ForceAuthority = EnsureRoot; + type MaxCandidatesPerShortlist = MaxCandidatesPerShortlist; + type ShortlistPeriod = ShortlistPeriod; + type MembershipDeposit = MembershipDeposit; + type DepositCurrencyId = DepositCurrencyId; + type SlashAccount = SlashAccount; + type Permissions = pallet_fellowship::impls::VetterAndFreelancerAllPermissions; + type WeightInfo = pallet_fellowship::weights::WeightInfo; +} + parameter_types! { pub const UnitWeightCost: u64 = 10; pub const MaxInstructions: u32 = 100; } + pub static ALICE: AccountId = 125; pub static BOB: AccountId = 126; pub static CHARLIE: AccountId = 127; +pub static FREELANCER: AccountId = 1270; pub static TREASURY: AccountId = 200; +pub static JURY_1: AccountId = 1002; +pub static JURY_2: AccountId = 1001; pub(crate) fn build_test_externality() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default() @@ -292,7 +321,7 @@ pub(crate) fn build_test_externality() -> sp_io::TestExternalities { .unwrap(); orml_tokens::GenesisConfig:: { balances: { - vec![ALICE, BOB, CHARLIE] + vec![ALICE, BOB, CHARLIE, FREELANCER] .into_iter() .map(|id| (id, CurrencyId::Native, 1000000)) .collect::>() @@ -303,6 +332,7 @@ pub(crate) fn build_test_externality() -> sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| { + pallet_fellowship::Roles::::insert(&FREELANCER, (Role::Freelancer, 10)); System::set_block_number(1); }); ext @@ -312,7 +342,9 @@ pub struct MockJurySelector; impl pallet_fellowship::traits::SelectJury for MockJurySelector { type JurySize = MaxJuryMembers; fn select_jury() -> BoundedVec { - BoundedVec::new() + vec![JURY_1, JURY_2] + .try_into() + .expect("should be below bound.") } } diff --git a/pallets/briefs/src/tests.rs b/pallets/briefs/src/tests.rs index 498479be..56320f59 100644 --- a/pallets/briefs/src/tests.rs +++ b/pallets/briefs/src/tests.rs @@ -6,16 +6,55 @@ use crate::*; use common_types::CurrencyId; use frame_support::{assert_noop, assert_ok, pallet_prelude::*}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; +use pallet_fellowship::traits::EnsureRole; use pallet_proposals::{BoundedProposedMilestones, Projects, ProposedMilestone}; use sp_arithmetic::per_things::Percent; +use sp_runtime::DispatchError::BadOrigin; use std::convert::TryInto; #[test] fn create_brief_not_approved_applicant() { build_test_externality().execute_with(|| { - // TODO: - // Only accounts in the fellowship can apply for work + assert_noop!( + BriefsMod::create_brief( + RuntimeOrigin::signed(BOB), + get_brief_owners(u32::MAX), + ALICE, + 100000, + 10000, + gen_hash(1), + CurrencyId::Native, + get_milestones(10), + None, + true, + ), + BadOrigin + ); + }); +} + +#[test] +fn create_brief_approved_applicant() { + build_test_externality().execute_with(|| { + assert_ok!(::EnsureRole::ensure_role( + &FREELANCER, + pallet_fellowship::Role::Freelancer, + None + )); + + assert_ok!(BriefsMod::create_brief( + RuntimeOrigin::signed(BOB), + get_brief_owners(10), + FREELANCER, + 100000, + 10000, + gen_hash(1), + CurrencyId::Native, + get_milestones(10), + None, + true, + )); }); } @@ -33,6 +72,7 @@ fn create_brief_brief_owner_overflow() { CurrencyId::Native, get_milestones(10), None, + false, ), Error::::TooManyBriefOwners ); @@ -52,6 +92,7 @@ fn create_brief_with_no_contribution_ok() { CurrencyId::Native, get_milestones(10), None, + false, )); }); } @@ -73,6 +114,7 @@ fn create_brief_no_contribution_and_contribute() { CurrencyId::Native, get_milestones(10), None, + false, )); (0..5).for_each(|_| { @@ -115,6 +157,7 @@ fn contribute_to_brief_not_brief_owner() { CurrencyId::Native, get_milestones(10), None, + false, )); assert_noop!( @@ -144,6 +187,7 @@ fn contribute_to_brief_more_than_total_ok() { CurrencyId::Native, get_milestones(10), None, + false, )); assert_ok!(BriefsMod::contribute_to_brief( RuntimeOrigin::signed(BOB), @@ -169,6 +213,7 @@ fn create_brief_already_exists() { CurrencyId::Native, get_milestones(10), None, + false, )); assert_noop!( @@ -182,6 +227,7 @@ fn create_brief_already_exists() { CurrencyId::Native, get_milestones(10), None, + false, ), Error::::BriefAlreadyExists ); @@ -204,6 +250,7 @@ fn only_applicant_can_start_work() { CurrencyId::Native, get_milestones(10), None, + false, )); assert_noop!( @@ -234,6 +281,7 @@ fn initial_contribution_and_extra_contribution_aggregates() { CurrencyId::Native, get_milestones(10), None, + false, )); assert_ok!(BriefsMod::contribute_to_brief( @@ -271,6 +319,7 @@ fn reserved_funds_are_transferred_to_project_kitty() { CurrencyId::Native, get_milestones(10), None, + false, ); assert_ok!(BriefsMod::commence_work( @@ -304,6 +353,7 @@ fn cancel_brief_works() { CurrencyId::Native, get_milestones(10), None, + false, )); assert_ok!(BriefsMod::contribute_to_brief( @@ -371,6 +421,7 @@ fn cancel_brief_not_brief_owner() { CurrencyId::Native, get_milestones(10), None, + false, )); assert_noop!( diff --git a/pallets/disputes/src/impls.rs b/pallets/disputes/src/impls.rs index 1393bdea..b9181d1d 100644 --- a/pallets/disputes/src/impls.rs +++ b/pallets/disputes/src/impls.rs @@ -3,7 +3,7 @@ use frame_support::BoundedVec; use sp_runtime::DispatchError; use crate::pallet::{AccountIdOf, Config, Dispute}; -use traits::DisputeRaiser; +use crate::traits::{DisputeHooks, DisputeRaiser}; impl DisputeRaiser> for Pallet { type DisputeKey = T::DisputeKey; diff --git a/pallets/fellowship/src/impls.rs b/pallets/fellowship/src/impls.rs index b8b5d8fd..26637312 100644 --- a/pallets/fellowship/src/impls.rs +++ b/pallets/fellowship/src/impls.rs @@ -8,7 +8,7 @@ use sp_std::{vec, vec::Vec}; /// Ensure that a account is of a given role. /// Used in other pallets like an ensure origin. pub struct EnsureFellowshipRole(T); -impl EnsureRole, Role> for EnsureFellowshipRole { +impl EnsureRole> for EnsureFellowshipRole { type Success = (); fn ensure_role( diff --git a/pallets/fellowship/src/traits.rs b/pallets/fellowship/src/traits.rs index de1cb52a..69b19495 100644 --- a/pallets/fellowship/src/traits.rs +++ b/pallets/fellowship/src/traits.rs @@ -1,4 +1,4 @@ -use crate::Rank; +use crate::{Rank, Role}; use codec::{FullCodec, FullEncode}; use frame_support::{pallet_prelude::*, weights::Weight}; use sp_runtime::DispatchError; @@ -19,7 +19,7 @@ pub trait FellowshipHandle { fn revoke_fellowship(who: &AccountId, slash_deposit: bool) -> Result<(), DispatchError>; } -pub trait EnsureRole { +pub trait EnsureRole { type Success; fn ensure_role( acc: &AccountId, diff --git a/pallets/proposals/src/benchmarking.rs b/pallets/proposals/src/benchmarking.rs index b3deb780..be1f3a07 100644 --- a/pallets/proposals/src/benchmarking.rs +++ b/pallets/proposals/src/benchmarking.rs @@ -29,6 +29,8 @@ mod benchmarks { create_funded_user::("contributor", 1, 1_000_000_000_000_000_000u128); let bob: T::AccountId = create_funded_user::("initiator", 1, 1_000_000_000_000_000_000u128); + let jury = get_funded_jury::(10); + let contributions = get_contributions::(vec![alice], 100_000_000_000_000_000u128); let prop_milestones = get_max_milestones::(); let project_key = create_and_fund_project::( @@ -36,6 +38,7 @@ mod benchmarks { contributions, prop_milestones, CurrencyId::Native, + jury, ) .unwrap(); @@ -50,6 +53,8 @@ mod benchmarks { create_funded_user::("initiator", 1, 1_000_000_000_000_000_000u128); let bob: T::AccountId = create_funded_user::("contributor", 1, 1_000_000_000_000_000_000u128); + let jury = get_funded_jury::(10); + // TODO: should update the contributors list to have maximum available length let contributions = get_contributions::(vec![bob.clone()], 1_000_000_000_000u128); let prop_milestones = get_max_milestones::(); @@ -58,6 +63,7 @@ mod benchmarks { contributions, prop_milestones, CurrencyId::Native, + jury, ) .unwrap(); @@ -81,9 +87,9 @@ mod benchmarks { create_funded_user::("initiator", 1, 1_000_000_000_000_000_000u128); let bob: T::AccountId = create_funded_user::("contributor", 1, 1_000_000_000_000_000_000u128); + let jury = get_funded_jury::(10); let contributions = get_contributions::(vec![bob.clone()], 100_000_000_000_000_000u128); let raised_funds = 100_000_000_000_000_000u128.saturated_into(); - let milestone_count = ::MaxMilestonesPerProject::get(); let prop_milestones = get_milestones(milestone_count as u8); @@ -92,6 +98,7 @@ mod benchmarks { contributions, prop_milestones, CurrencyId::Native, + jury, ) .unwrap(); @@ -145,6 +152,7 @@ mod benchmarks { create_funded_user::("initiator", 1, 1_000_000_000_000_000_000u128); let bob: T::AccountId = create_funded_user::("contributor", 0, 1_000_000_000_000_000_000u128); + let jury = get_funded_jury::(10); let contributors: Vec = (0 ..::MaximumContributorsPerProject::get()) @@ -160,9 +168,14 @@ mod benchmarks { .try_into() .unwrap(); - let project_key = - create_and_fund_project::(alice, contributions, prop_milestones, CurrencyId::Native) - .unwrap(); + let project_key = create_and_fund_project::( + alice, + contributions, + prop_milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); #[extrinsic_call] raise_dispute(RawOrigin::Signed(bob), project_key, milestone_keys); @@ -175,6 +188,7 @@ mod benchmarks { create_funded_user::("initiator", 1, 1_000_000_000_000_000_000u128); let bob: T::AccountId = create_funded_user::("contributor", 0, 1_000_000_000_000_000_000u128); + let jury = get_funded_jury::(10); let contributors: Vec = (0 ..::MaximumContributorsPerProject::get()) @@ -192,9 +206,14 @@ mod benchmarks { .try_into() .unwrap(); - let project_key = - create_and_fund_project::(alice, contributions, prop_milestones, CurrencyId::Native) - .unwrap(); + let project_key = create_and_fund_project::( + alice, + contributions, + prop_milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); assert_ok!(crate::Pallet::::raise_dispute( RawOrigin::Signed(bob.clone()).into(), @@ -224,3 +243,9 @@ mod benchmarks { crate::mock::Test ); } + +fn get_funded_jury(n: u32) -> Vec> { + (0..n) + .map(|n| create_funded_user::("jury member", n, 1_000_000_000_000_000_000u128)) + .collect::>>() +} diff --git a/pallets/proposals/src/impls/pallet_impls.rs b/pallets/proposals/src/impls/pallet_impls.rs index 8685fda8..6c126738 100644 --- a/pallets/proposals/src/impls/pallet_impls.rs +++ b/pallets/proposals/src/impls/pallet_impls.rs @@ -316,7 +316,13 @@ impl Pallet { let exp_block = Rounds::::take((project_key, milestone_key), RoundType::VotingRound) .ok_or(Error::::VotingRoundNotStarted)?; // Prevent hook from calling. + // TODO: only remove project key???? RoundsExpiring::::remove(exp_block); + + MilestoneVotes::::mutate(project_key, |btree_votes| { + let _val = btree_votes.remove(&milestone_key); + }); + // Allow future votes to occur on this milestone IndividualVoteStore::::try_mutate(project_key, |maybe_individual_votes| { if let Some(individual_votes) = maybe_individual_votes { diff --git a/pallets/proposals/src/lib.rs b/pallets/proposals/src/lib.rs index 752c2299..ac350a70 100644 --- a/pallets/proposals/src/lib.rs +++ b/pallets/proposals/src/lib.rs @@ -354,6 +354,8 @@ pub mod pallet { OnlyContributorsCanInitiateRefund, /// Only the ForeignAssetSigner can mint tokens RequireForeignAssetSigner, + /// A Jury is required to create a project. + JuryRequired, } #[pallet::hooks] @@ -468,13 +470,18 @@ pub mod pallet { Error::::CannotRaiseDisputeOnApprovedMilestone ); - ::DisputeRaiser::raise_dispute( - project_key, - who, - project.jury, - milestone_keys.clone(), - )?; - ProjectsInDispute::::insert(project_key, milestone_keys); + if project.jury.len() == 1 { + // https://github.com/ImbueNetwork/imbue/issues/270 + let _ = >::on_dispute_complete(project_key, milestone_keys.to_vec(), pallet_disputes::DisputeResult::Success); + } else { + ::DisputeRaiser::raise_dispute( + project_key, + who, + project.jury, + milestone_keys.clone(), + )?; + ProjectsInDispute::::insert(project_key, milestone_keys); + } Ok(()) } @@ -642,6 +649,7 @@ pub mod pallet { on_creation_funding: FundingPath, eoa: Option, ) -> Result<(), DispatchError> { + ensure!(jury.len() > 0, Error::::JuryRequired); let project_key = crate::ProjectCount::::get().saturating_add(1); // Take storage deposit only for a Project. diff --git a/pallets/proposals/src/migration.rs b/pallets/proposals/src/migration.rs index 33946a6d..16a45f9f 100644 --- a/pallets/proposals/src/migration.rs +++ b/pallets/proposals/src/migration.rs @@ -1162,9 +1162,15 @@ mod test { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, DAVE], 100_000); let prop_milestones = get_milestones(10); - let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) - .expect("project wasnt created!"); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( + ALICE, + cont, + prop_milestones, + CurrencyId::Native, + jury, + ) + .expect("project wasnt created!"); let milestone_key: MilestoneKey = 0; let expiry_block: BlockNumber = 10; let rounds_expiring: BoundedProjectKeysPerBlock = diff --git a/pallets/proposals/src/mock.rs b/pallets/proposals/src/mock.rs index db32a2ab..8ee00050 100644 --- a/pallets/proposals/src/mock.rs +++ b/pallets/proposals/src/mock.rs @@ -229,6 +229,9 @@ pub static DAVE: AccountId = 128; pub static TREASURY: AccountId = 222; pub static JOHN: AccountId = 255; +pub static JURY_1: AccountId = 1000; +pub static JURY_2: AccountId = 1001; + pub(crate) fn build_test_externality() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default() .build_storage() diff --git a/pallets/proposals/src/test_utils.rs b/pallets/proposals/src/test_utils.rs index eceece11..6803a5d9 100644 --- a/pallets/proposals/src/test_utils.rs +++ b/pallets/proposals/src/test_utils.rs @@ -67,6 +67,7 @@ pub fn create_and_fund_project( contributions: ContributionsFor, proposed_milestones: Vec, currency_id: CurrencyId, + jury: Vec>, ) -> Result { contributions.iter().for_each(|(acc, c)| { ::MultiCurrency::reserve(currency_id, acc, c.value).unwrap(); @@ -86,7 +87,7 @@ pub fn create_and_fund_project( beneficiary, proposed_milestones.try_into().map_err(|_|Error::::TooManyMilestones)?, refund_locations, - BoundedVec::new(), + jury.try_into().expect("Too many Jury members."), FundingPath::TakeFromReserved, None, )?; diff --git a/pallets/proposals/src/tests/disputes.rs b/pallets/proposals/src/tests/disputes.rs index 3106528e..87a76afe 100644 --- a/pallets/proposals/src/tests/disputes.rs +++ b/pallets/proposals/src/tests/disputes.rs @@ -9,11 +9,14 @@ fn raise_dispute_not_contributor() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -48,11 +51,14 @@ fn raise_dispute_milestone_already_in_dispute() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -83,9 +89,16 @@ fn raise_dispute_invalid_milestone_key() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); - let project_key = - create_and_fund_project::(ALICE, contributions, milestones, CurrencyId::Native) - .unwrap(); + let jury = vec![JURY_1, JURY_2]; + + let project_key = create_and_fund_project::( + ALICE, + contributions, + milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); assert_noop!( Proposals::raise_dispute( RuntimeOrigin::signed(BOB), @@ -118,9 +131,16 @@ fn raise_dispute_cant_raise_on_approved_milestone() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); - let project_key = - create_and_fund_project::(ALICE, contributions, milestones, CurrencyId::Native) - .unwrap(); + let jury = vec![JURY_1, JURY_2]; + + let project_key = create_and_fund_project::( + ALICE, + contributions, + milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); let submitted_milestone_key = 0u32; assert_ok!(Proposals::submit_milestone( @@ -165,11 +185,14 @@ fn on_dispute_complete_success_removes_dispute_status() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -196,11 +219,14 @@ fn on_dispute_complete_failure_removes_dispute_status() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -227,11 +253,14 @@ fn dispute_success_does_not_cancel_project() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -261,11 +290,14 @@ fn dispute_success_approves_milestone_for_refund_but_only_ones_specified() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (1u32 @@ -293,11 +325,14 @@ fn raise_dispute_allows_milestone_voting() { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); let submitted_milestone_key = 0; + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); @@ -338,11 +373,14 @@ fn raise_dispute_allows_milestone_voting_on_non_disputed_milestones() { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); let submitted_milestone_keys = [0, 1]; + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); @@ -390,11 +428,14 @@ fn raise_dispute_allows_submission() { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); let milestone_key = 0; + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); @@ -422,11 +463,14 @@ fn failed_dispute_tests() { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); let _milestone_key = 0; + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); @@ -461,9 +505,16 @@ fn assert_can_recall_dispute_after_success() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); - let project_key = - create_and_fund_project::(ALICE, contributions, milestones, CurrencyId::Native) - .unwrap(); + let jury = vec![JURY_1, JURY_2]; + + let project_key = create_and_fund_project::( + ALICE, + contributions, + milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); // Only call the dispute on part. let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32..5_u32).collect::>().try_into().unwrap(); @@ -493,9 +544,16 @@ fn assert_can_recall_dispute_after_failure() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); - let project_key = - create_and_fund_project::(ALICE, contributions, milestones, CurrencyId::Native) - .unwrap(); + let jury = vec![JURY_1, JURY_2]; + + let project_key = create_and_fund_project::( + ALICE, + contributions, + milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); // Only call the dispute on part. let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32..5_u32).collect::>().try_into().unwrap(); @@ -519,3 +577,53 @@ fn assert_can_recall_dispute_after_failure() { )); }) } + +#[test] +fn raise_dispute_with_single_jury_auto_completes() { + build_test_externality().execute_with(|| { + let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); + let milestones = get_milestones(10); + let jury = vec![JURY_1]; + + let project_key = create_and_fund_project::( + ALICE, + contributions, + milestones.clone(), + CurrencyId::Native, + jury, + ) + .unwrap(); + + let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 + ..milestones.len() as u32) + .collect::>() + .try_into() + .unwrap(); + + assert_ok!(Proposals::raise_dispute( + RuntimeOrigin::signed(BOB), + project_key, + milestone_keys.clone() + )); + + let project = Projects::::get(project_key).unwrap(); + + project.milestones.iter().for_each(|(key, ms)| { + // if its meant to be approved for refund then check + if milestone_keys.contains(key) { + assert!( + ms.can_refund, + "the dispute should have bypassed dispute pallet and autofinalised." + ); + } else { + assert!( + !ms.can_refund, + "This milestone should not have been approved for refund." + ); + } + + // They havent been transferred yet + assert_eq!(ms.transfer_status, None) + }); + }) +} diff --git a/pallets/proposals/src/tests/foreign_asset.rs b/pallets/proposals/src/tests/foreign_asset.rs new file mode 100644 index 00000000..6d4bb6f1 --- /dev/null +++ b/pallets/proposals/src/tests/foreign_asset.rs @@ -0,0 +1,80 @@ +use crate::{mock::*, *}; +use common_types::ForeignAssetId; +use frame_support::{assert_noop, assert_ok, error::BadOrigin}; +use test_utils::*; + +#[test] +fn set_foreign_asset_signer_check_permission_for_edit() { + build_test_externality().execute_with(|| { + assert_ok!(Proposals::set_foreign_asset_signer( + RuntimeOrigin::root(), + ALICE + )); + assert_eq!( + ForeignCurrencySigner::::get().unwrap(), + ALICE, + "Alice should have been set as signer." + ); + assert_ok!(Proposals::set_foreign_asset_signer( + RuntimeOrigin::root(), + BOB + )); + assert_eq!( + ForeignCurrencySigner::::get().unwrap(), + BOB, + "Bob should be set as signer." + ); + assert_noop!( + Proposals::set_foreign_asset_signer(RuntimeOrigin::signed(BOB), ALICE), + BadOrigin + ); + }) +} + +#[test] +fn foreign_asset_signer_can_mint() { + build_test_externality().execute_with(|| { + let currency_id = CurrencyId::ForeignAsset(ForeignAssetId::ETH); + let beneficiary = BOB; + let amount = 92839572; + let _ = Proposals::set_foreign_asset_signer(RuntimeOrigin::root(), ALICE); + let asset_signer = ForeignCurrencySigner::::get().unwrap(); + assert_eq!(Tokens::free_balance(currency_id, &BOB), 0); + assert_ok!(Proposals::mint_offchain_assets( + RuntimeOrigin::signed(asset_signer), + beneficiary, + currency_id, + amount + )); + assert_eq!(Tokens::free_balance(currency_id, &BOB), amount); + }) +} + +#[test] +fn non_foreign_asset_signer_cannot_mint() { + build_test_externality().execute_with(|| { + let currency_id = CurrencyId::ForeignAsset(ForeignAssetId::ETH); + let beneficiary = BOB; + let amount = 92839572; + let _ = Proposals::set_foreign_asset_signer(RuntimeOrigin::root(), ALICE); + + assert_noop!( + Proposals::mint_offchain_assets( + RuntimeOrigin::signed(BOB), + beneficiary, + currency_id, + amount + ), + Error::::RequireForeignAssetSigner + ); + assert_noop!( + Proposals::mint_offchain_assets( + RuntimeOrigin::signed(CHARLIE), + beneficiary, + currency_id, + amount + ), + Error::::RequireForeignAssetSigner + ); + }) +} diff --git a/pallets/proposals/src/tests/mod.rs b/pallets/proposals/src/tests/mod.rs index 0eeec162..8c67f181 100644 --- a/pallets/proposals/src/tests/mod.rs +++ b/pallets/proposals/src/tests/mod.rs @@ -1,4 +1,5 @@ pub mod disputes; +pub mod foreign_asset; pub mod immutable_votes; pub mod pallet; pub mod refunds; diff --git a/pallets/proposals/src/tests/pallet.rs b/pallets/proposals/src/tests/pallet.rs index d7b85006..0e9bbb51 100644 --- a/pallets/proposals/src/tests/pallet.rs +++ b/pallets/proposals/src/tests/pallet.rs @@ -21,8 +21,10 @@ fn submit_milestone_milestone_doesnt_exist() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_noop!( Proposals::submit_milestone(RuntimeOrigin::signed(ALICE), project_key, 11), @@ -46,8 +48,10 @@ fn submit_milestone_not_initiator() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_noop!( Proposals::submit_milestone(RuntimeOrigin::signed(BOB), project_key, 1), @@ -66,6 +70,7 @@ fn submit_milestones_too_many_this_block() { let max = ::ExpiringProjectRoundsPerBlock::get(); let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; (0..=max).for_each(|i| { let project_key = create_and_fund_project::( @@ -73,6 +78,7 @@ fn submit_milestones_too_many_this_block() { cont.clone(), prop_milestones.clone(), CurrencyId::Native, + jury.clone(), ) .unwrap(); if i != max { @@ -96,8 +102,10 @@ fn submit_milestone_creates_non_bias_vote() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_ok!(Proposals::submit_milestone( RuntimeOrigin::signed(ALICE), @@ -117,8 +125,10 @@ fn submit_milestone_can_resubmit_during_voting_round() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -163,8 +173,10 @@ fn submit_milestone_can_submit_again_after_failed_vote() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_ok!(Proposals::submit_milestone( RuntimeOrigin::signed(ALICE), @@ -187,8 +199,10 @@ fn submit_milestone_cannot_submit_again_after_success_vote() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -224,8 +238,10 @@ fn ensure_milestone_vote_data_is_cleaned_after_autofinalisation_for() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -291,8 +307,10 @@ fn ensure_milestone_vote_data_is_cleaned_after_autofinalisation_against() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -357,8 +375,10 @@ fn users_can_submit_multiple_milestones_and_vote_independantly() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key_0 = 0; let milestone_key_1 = 1; @@ -416,8 +436,10 @@ fn vote_on_milestone_before_round_starts_fails() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_noop!( @@ -437,8 +459,10 @@ fn vote_on_milestone_after_round_end_fails() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; let expiring_block = frame_system::Pallet::::block_number() @@ -466,8 +490,10 @@ fn vote_on_milestone_where_voting_round_is_active_but_not_the_correct_milestone( build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_ok!(Proposals::submit_milestone( RuntimeOrigin::signed(ALICE), @@ -486,8 +512,10 @@ fn if_double_submission_and_one_finalises_voting_on_the_second_can_vote() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let expiring_block = frame_system::Pallet::::block_number() + ::MilestoneVotingWindow::get(); @@ -517,8 +545,10 @@ fn vote_on_milestone_not_contributor() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -541,10 +571,12 @@ fn vote_on_milestone_not_contributor() { #[test] fn vote_on_milestone_actually_adds_to_vote() { build_test_externality().execute_with(|| { - let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); + let cont = get_contributions::(vec![BOB, CHARLIE, DAVE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -581,8 +613,10 @@ fn vote_on_milestone_autofinalises_on_all_voted_and_fail() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE, DAVE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -615,13 +649,89 @@ fn vote_on_milestone_autofinalises_on_all_voted_and_fail() { }); } +#[test] +fn vote_struct_removed_on_autofinalisation_success() { + build_test_externality().execute_with(|| { + let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); + let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) + .unwrap(); + let milestone_key = 5; + assert_ok!(Proposals::submit_milestone( + RuntimeOrigin::signed(ALICE), + project_key, + milestone_key + )); + assert_ok!(Proposals::vote_on_milestone( + RuntimeOrigin::signed(BOB), + project_key, + milestone_key, + true + )); + assert!(MilestoneVotes::::get(project_key) + .get(&milestone_key) + .is_some()); + + assert_ok!(Proposals::vote_on_milestone( + RuntimeOrigin::signed(CHARLIE), + project_key, + milestone_key, + true + )); + assert!(MilestoneVotes::::get(project_key) + .get(&milestone_key) + .is_none()); + }); +} + +#[test] +fn vote_struct_removed_on_autofinalisation_failure() { + build_test_externality().execute_with(|| { + let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); + let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) + .unwrap(); + let milestone_key = 5; + assert_ok!(Proposals::submit_milestone( + RuntimeOrigin::signed(ALICE), + project_key, + milestone_key + )); + assert_ok!(Proposals::vote_on_milestone( + RuntimeOrigin::signed(BOB), + project_key, + milestone_key, + false + )); + assert!(MilestoneVotes::::get(project_key) + .get(&milestone_key) + .is_some()); + assert_ok!(Proposals::vote_on_milestone( + RuntimeOrigin::signed(CHARLIE), + project_key, + milestone_key, + false + )); + + assert!(MilestoneVotes::::get(project_key) + .get(&milestone_key) + .is_none()); + }); +} + #[test] fn withdraw_not_initiator() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB, CHARLIE], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_ok!(Proposals::submit_milestone( @@ -659,8 +769,10 @@ fn withdraw_only_transfers_approved_milestones() { let per_contribution = 100_000; let cont = get_contributions::(vec![BOB, CHARLIE], per_contribution); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let project_account = crate::Pallet::::project_account_id(project_key); let milestone_key = 0; @@ -708,8 +820,10 @@ fn withdraw_removes_project_after_all_funds_taken() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(1); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; let _ = @@ -740,8 +854,10 @@ fn store_project_info_after_project_is_completed() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(1); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; let _ = @@ -773,6 +889,8 @@ fn store_too_many_projects_for_account() { let max = ::MaxProjectsPerAccount::get(); let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(1); + let jury = vec![JURY_1, JURY_2]; + let milestone_key = 0; (0..=max).for_each(|i| { let project_key = create_and_fund_project::( @@ -780,6 +898,7 @@ fn store_too_many_projects_for_account() { cont.clone(), prop_milestones.clone(), CurrencyId::Native, + jury.clone(), ) .unwrap(); let _ = Proposals::submit_milestone( @@ -816,8 +935,10 @@ fn withdraw_takes_imbue_fee() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; let fee_account: AccountId = ::ImbueFeeAccount::get(); @@ -855,8 +976,10 @@ fn withdraw_cannot_double_withdraw() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; let _ = @@ -885,8 +1008,10 @@ fn withdraw_once_times_with_double_submissions() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let _ = Proposals::submit_milestone(RuntimeOrigin::signed(ALICE), project_key, 0).unwrap(); let _ = @@ -917,8 +1042,10 @@ fn withdraw_twice_with_intermitent_submission() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); // The first submission and withdraw @@ -971,8 +1098,10 @@ fn withdraw_with_variable_percentage() { percentage_to_unlock: Percent::from_percent(30u8), }, ]; + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let _ = Proposals::submit_milestone(RuntimeOrigin::signed(ALICE), project_key, 0).unwrap(); let _ = @@ -998,8 +1127,10 @@ fn withdraw_fails_before_approval() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_noop!( @@ -1021,8 +1152,10 @@ fn withdraw_assert_milestone_state_change() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); let milestone_key = 0; assert_noop!( @@ -1079,8 +1212,10 @@ fn project_in_voting_is_saved_on_submission() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_ok!(Proposals::submit_milestone( @@ -1104,8 +1239,10 @@ fn project_in_voting_is_removed_on_init_hook() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_ok!(Proposals::submit_milestone( @@ -1130,8 +1267,10 @@ fn project_in_voting_is_removed_on_milestone_autofinalisation() { build_test_externality().execute_with(|| { let cont = get_contributions::(vec![BOB], 100_000); let prop_milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = - create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native) + create_and_fund_project::(ALICE, cont, prop_milestones, CurrencyId::Native, jury) .unwrap(); assert_ok!(Proposals::submit_milestone( diff --git a/pallets/proposals/src/tests/refunds.rs b/pallets/proposals/src/tests/refunds.rs index c61c6dab..bca95eec 100644 --- a/pallets/proposals/src/tests/refunds.rs +++ b/pallets/proposals/src/tests/refunds.rs @@ -8,11 +8,14 @@ fn you_can_actually_refund_after_dispute_success() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -41,9 +44,16 @@ fn refund_assert_milestone_state_change() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB], 1_000_000u128); let milestones = get_milestones(10); - let project_key = - create_and_fund_project::(ALICE, contributions, milestones, CurrencyId::Native) - .unwrap(); + let jury = vec![JURY_1, JURY_2]; + + let project_key = create_and_fund_project::( + ALICE, + contributions, + milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); // Only dispute some keys so that we can let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32..5_u32).collect::>().try_into().unwrap(); @@ -85,11 +95,14 @@ fn refund_not_contributor() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -119,11 +132,14 @@ fn refund_deletes_project_when_all_funds_are_refunded() { build_test_externality().execute_with(|| { let contributions = get_contributions::(vec![BOB, CHARLIE], 1_000_000u128); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -161,11 +177,14 @@ fn withdraw_then_refund_no_double_spend() { ::MultiCurrency::free_balance(CurrencyId::Native, &ALICE); let bob_before_creation = ::MultiCurrency::free_balance(CurrencyId::Native, &ALICE); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -231,9 +250,16 @@ fn refund_then_withdraw_no_double_spend() { ::MultiCurrency::free_balance(CurrencyId::Native, &ALICE); let _bob_before_creation = ::MultiCurrency::free_balance(CurrencyId::Native, &ALICE); - let project_key = - create_and_fund_project::(ALICE, contributions, milestones, CurrencyId::Native) - .unwrap(); + let jury = vec![JURY_1, JURY_2]; + + let project_key = create_and_fund_project::( + ALICE, + contributions, + milestones, + CurrencyId::Native, + jury, + ) + .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32..5_u32).collect::>().try_into().unwrap(); let _ = Proposals::raise_dispute( @@ -275,11 +301,14 @@ fn refund_check_refund_amount() { let per_contribution = 100000u128; let contributions = get_contributions::(vec![BOB, CHARLIE], per_contribution); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 @@ -333,11 +362,14 @@ fn refund_takes_imbue_fee() { let contributions = get_contributions::(vec![BOB, CHARLIE], per_contribution); let milestones = get_milestones(10); + let jury = vec![JURY_1, JURY_2]; + let project_key = create_and_fund_project::( ALICE, contributions, milestones.clone(), CurrencyId::Native, + jury, ) .unwrap(); let milestone_keys: BoundedVec::MaxMilestonesPerProject> = (0u32 diff --git a/runtime/imbue-kusama/src/lib.rs b/runtime/imbue-kusama/src/lib.rs index 1087e827..23a387cf 100644 --- a/runtime/imbue-kusama/src/lib.rs +++ b/runtime/imbue-kusama/src/lib.rs @@ -101,7 +101,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("imbue"), impl_name: create_runtime_str!("imbue"), authoring_version: 2, - spec_version: 1_000_001, + spec_version: 1_100_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, @@ -869,6 +869,7 @@ impl pallet_briefs::Config for Runtime { type BriefStorageItem = BriefStorageItem; type DepositHandler = Deposits; type JurySelector = PointerBasedJurySelector; + type EnsureRole = pallet_fellowship::impls::EnsureFellowshipRole; } parameter_types! {