diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58b35af0..122b0235 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,15 +17,6 @@ env: GCP_ZONE: europe-west3-a jobs: - check_branch: - runs-on: ubuntu-latest - steps: - - name: Check branch - if: github.base_ref == 'main' && github.head_ref != 'staging' - run: | - echo "ERROR: You can only merge to main from staging." - exit 1 - create-runner: runs-on: ubuntu-latest outputs: @@ -40,8 +31,8 @@ jobs: image_project: ubuntu-os-cloud image_family: ubuntu-2004-lts machine_type: e2-highcpu-32 - disk_size: 100 machine_zone: ${{ env.GCP_ZONE }} + disk_size: 100 ephemeral: true test-features: diff --git a/.github/workflows/check_branch.yml b/.github/workflows/check_branch.yml new file mode 100644 index 00000000..950af1da --- /dev/null +++ b/.github/workflows/check_branch.yml @@ -0,0 +1,25 @@ + +name: check-branch +on: + workflow_dispatch: + push: + branches: + - main + paths-ignore: + - "**.md" + pull_request: + branches: + - main + - staging + paths-ignore: + - "**.md" + +jobs: + check_branch: + runs-on: ubuntu-latest + steps: + - name: Check branch + if: github.base_ref == 'main' && github.head_ref != 'staging' + run: | + echo "ERROR: You can only merge to main from staging." + exit 1 \ No newline at end of file diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml index 7c7054f7..0b93e2b1 100644 --- a/.github/workflows/fmt.yml +++ b/.github/workflows/fmt.yml @@ -9,18 +9,16 @@ on: pull_request: branches: - main + - staging paths-ignore: - "**.md" jobs: cargo-fmt: - runs-on: ${{ needs.create-runner.outputs.label }} + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set HOME - run: echo "HOME=/home/ubuntu" >> ${GITHUB_ENV} - - name: Install minimal nightly Rust uses: actions-rs/toolchain@v1 with: diff --git a/.github/workflows/try_runtime.yml b/.github/workflows/try_runtime.yml new file mode 100644 index 00000000..73c9627b --- /dev/null +++ b/.github/workflows/try_runtime.yml @@ -0,0 +1,70 @@ +name: try-runtime +on: + workflow_dispatch: + push: + branches: + - main + paths-ignore: + - "**.md" + pull_request: + branches: + - main + - staging + paths-ignore: + - "**.md" +env: + CARGO_TERM_COLOR: always + GCP_ZONE: europe-west3-b + +jobs: + create-runner: + runs-on: ubuntu-latest + outputs: + label: ${{ steps.create-runner.outputs.label }} + steps: + - id: create-runner + uses: related-sciences/gce-github-runner@v0.10 + with: + token: ${{ secrets.GH_SA_TOKEN }} + project_id: ${{ secrets.GCP_PROJECT_ID }} + service_account_key: ${{ secrets.GCP_SA_KEY }} + image_project: ubuntu-os-cloud + image_family: ubuntu-2004 + machine_type: e2-highcpu-32 + machine_zone: ${{ env.GCP_ZONE }} + disk_size: 100 + ephemeral: true + + try-runtime: + needs: create-runner + runs-on: ${{ needs.create-runner.outputs.label }} + steps: + - uses: actions/checkout@v4 + + - name: Set HOME + run: echo "HOME=/home/ubuntu" >> ${GITHUB_ENV} + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: default + toolchain: stable + target: wasm32-unknown-unknown + override: true + + - name: Install Dependencies + run: sudo apt install protobuf-compiler clang build-essential -y + + - name: Install try-runtime + run: cargo install --git https://github.com/paritytech/try-runtime-cli --locked + + - name: Build binary with try-runtime + run: cargo build --features try-runtime + + - name: run imbue kusama try-runtime + run: try-runtime --runtime ./target/debug/wbuild/imbue-kusama-runtime/imbue_kusama_runtime.wasm on-runtime-upgrade live --uri ws://34.123.139.91:9942 + + - name: run imbue rococo try-runtime + run: try-runtime --runtime ./target/debug/wbuild/imbue-kusama-runtime/imbue_kusama_runtime.wasm on-runtime-upgrade live --uri ws://35.202.116.137:9942 + + diff --git a/pallets/briefs/src/benchmarking.rs b/pallets/briefs/src/benchmarking.rs index 250f1889..738e3810 100644 --- a/pallets/briefs/src/benchmarking.rs +++ b/pallets/briefs/src/benchmarking.rs @@ -41,6 +41,7 @@ mod benchmarks { brief_id, CurrencyId::Native, milestones, + false, ); assert_last_event::(Event::::BriefSubmitted(caller, brief_id).into()); } @@ -63,7 +64,8 @@ mod benchmarks { initial_contribution, brief_id, CurrencyId::Native, - milestones + milestones, + false, )); let brief_owner: T::AccountId = brief_owners[0].clone(); // (brief_owner, brief_id, contribution) @@ -93,7 +95,8 @@ mod benchmarks { initial_contribution, brief_id, CurrencyId::Native, - milestones + milestones, + false, )); // (origin, brief_id) #[extrinsic_call] @@ -118,7 +121,8 @@ mod benchmarks { initial_contribution, brief_id, CurrencyId::Native, - milestones + milestones, + false, )); // (origin, brief_id) #[extrinsic_call] diff --git a/pallets/briefs/src/integration_tests.rs b/pallets/briefs/src/integration_tests.rs index 02073748..798c2e55 100644 --- a/pallets/briefs/src/integration_tests.rs +++ b/pallets/briefs/src/integration_tests.rs @@ -22,6 +22,7 @@ fn create_proposal_from_brief() { brief_id, CurrencyId::Native, get_milestones(10), + false, ); assert_ok!(BriefsMod::commence_work( diff --git a/pallets/briefs/src/lib.rs b/pallets/briefs/src/lib.rs index 88588a5f..0527f536 100644 --- a/pallets/briefs/src/lib.rs +++ b/pallets/briefs/src/lib.rs @@ -31,6 +31,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}; @@ -82,9 +83,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; } @@ -170,9 +174,14 @@ pub mod pallet { brief_id: BriefHash, currency_id: CurrencyId, milestones: BoundedProposedMilestones, + 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/mock.rs b/pallets/briefs/src/mock.rs index 2d67a24b..96a84f44 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,13 +277,36 @@ 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(crate) fn build_test_externality() -> sp_io::TestExternalities { @@ -292,7 +319,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 +330,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 diff --git a/pallets/briefs/src/tests.rs b/pallets/briefs/src/tests.rs index 8470dfe3..09600553 100644 --- a/pallets/briefs/src/tests.rs +++ b/pallets/briefs/src/tests.rs @@ -6,16 +6,53 @@ 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), + 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), + true, + )); }); } @@ -32,6 +69,7 @@ fn create_brief_brief_owner_overflow() { gen_hash(1), CurrencyId::Native, get_milestones(10), + false, ), Error::::TooManyBriefOwners ); @@ -50,6 +88,7 @@ fn create_brief_with_no_contribution_ok() { gen_hash(1), CurrencyId::Native, get_milestones(10), + false, )); }); } @@ -70,6 +109,7 @@ fn create_brief_no_contribution_and_contribute() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); (0..5).for_each(|_| { @@ -111,6 +151,7 @@ fn contribute_to_brief_not_brief_owner() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); assert_noop!( @@ -139,6 +180,7 @@ fn contribute_to_brief_more_than_total_ok() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); assert_ok!(BriefsMod::contribute_to_brief( RuntimeOrigin::signed(BOB), @@ -163,6 +205,7 @@ fn create_brief_already_exists() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); assert_noop!( @@ -175,6 +218,7 @@ fn create_brief_already_exists() { brief_id, CurrencyId::Native, get_milestones(10), + false, ), Error::::BriefAlreadyExists ); @@ -196,6 +240,7 @@ fn only_applicant_can_start_work() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); assert_noop!( @@ -225,6 +270,7 @@ fn initial_contribution_and_extra_contribution_aggregates() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); assert_ok!(BriefsMod::contribute_to_brief( @@ -261,6 +307,7 @@ fn reserved_funds_are_transferred_to_project_kitty() { brief_id, CurrencyId::Native, get_milestones(10), + false, ); assert_ok!(BriefsMod::commence_work( @@ -293,6 +340,7 @@ fn cancel_brief_works() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); assert_ok!(BriefsMod::contribute_to_brief( @@ -359,6 +407,7 @@ fn cancel_brief_not_brief_owner() { brief_id, CurrencyId::Native, get_milestones(10), + false, )); assert_noop!( 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/runtime/imbue-kusama/src/lib.rs b/runtime/imbue-kusama/src/lib.rs index 0b7e9425..29a4b1bb 100644 --- a/runtime/imbue-kusama/src/lib.rs +++ b/runtime/imbue-kusama/src/lib.rs @@ -868,6 +868,7 @@ impl pallet_briefs::Config for Runtime { type BriefStorageItem = BriefStorageItem; type DepositHandler = Deposits; type JurySelector = PointerBasedJurySelector; + type EnsureRole = pallet_fellowship::impls::EnsureFellowshipRole; } parameter_types! {