lib.rs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. //! # Proposals codex module
  2. //! Proposals `codex` module for the Joystream platform. Version 3.
  3. //! Component of the proposals system. It contains preset proposal types.
  4. //!
  5. //! ## Overview
  6. //!
  7. //! The proposals codex module serves as a facade and entry point of the proposals system. It uses
  8. //! proposals `engine` module to maintain a lifecycle of the proposal and to execute proposals.
  9. //! During the proposal creation, `codex` also create a discussion thread using the `discussion`
  10. //! proposals module. `Codex` uses predefined parameters (eg.:`voting_period`) for each proposal and
  11. //! encodes extrinsic calls from dependency modules in order to create proposals inside the `engine`
  12. //! module. For each proposal, [its crucial details](./enum.ProposalDetails.html) are saved to the
  13. //! `ProposalDetailsByProposalId` map.
  14. //!
  15. //! ### General proposals
  16. //! - [create_text_proposal](./struct.Module.html#method.create_text_proposal)
  17. //! - [create_runtime_upgrade_proposal](./struct.Module.html#method.create_runtime_upgrade_proposal)
  18. //! - [create_set_validator_count_proposal](./struct.Module.html#method.create_set_validator_count_proposal)
  19. //!
  20. //! ### Council and election proposals
  21. //! - [create_set_election_parameters_proposal](./struct.Module.html#method.create_set_election_parameters_proposal)
  22. //! - [create_spending_proposal](./struct.Module.html#method.create_spending_proposal)
  23. //!
  24. //! ### Working group proposals
  25. //! - [create_add_working_group_leader_opening_proposal](./struct.Module.html#method.create_add_working_group_leader_opening_proposal)
  26. //! - [create_begin_review_working_group_leader_applications_proposal](./struct.Module.html#method.create_begin_review_working_group_leader_applications_proposal)
  27. //! - [create_fill_working_group_leader_opening_proposal](./struct.Module.html#method.create_fill_working_group_leader_opening_proposal)
  28. //! - [create_set_working_group_budget_capacity_proposal](./struct.Module.html#method.create_set_working_group_budget_capacity_proposal)
  29. //! - [create_decrease_working_group_leader_stake_proposal](./struct.Module.html#method.create_decrease_working_group_leader_stake_proposal)
  30. //! - [create_slash_working_group_leader_stake_proposal](./struct.Module.html#method.create_slash_working_group_leader_stake_proposal)
  31. //! - [create_set_working_group_leader_reward_proposal](./struct.Module.html#method.create_set_working_group_leader_reward_proposal)
  32. //! - [create_terminate_working_group_leader_role_proposal](./struct.Module.html#method.create_terminate_working_group_leader_role_proposal)
  33. //!
  34. //! ### Proposal implementations of this module
  35. //! - execute_text_proposal - prints the proposal to the log
  36. //! - execute_runtime_upgrade_proposal - sets the runtime code
  37. //!
  38. //! ### Dependencies:
  39. //! - [proposals engine](../substrate_proposals_engine_module/index.html)
  40. //! - [proposals discussion](../substrate_proposals_discussion_module/index.html)
  41. //! - [membership](../substrate_membership_module/index.html)
  42. //! - [governance](../substrate_governance_module/index.html)
  43. //!
  44. //! ### Notes
  45. //! The module uses [ProposalEncoder](./trait.ProposalEncoder.html) to encode the proposal using
  46. //! its details. Encoded byte vector is passed to the _proposals engine_ as serialized executable code.
  47. // `decl_module!` does a lot of recursion and requires us to increase the limit to 256.
  48. #![recursion_limit = "256"]
  49. // Ensure we're `no_std` when compiling for Wasm.
  50. #![cfg_attr(not(feature = "std"), no_std)]
  51. // Disable this lint warning because Substrate generates function without an alias for the ProposalDetailsOf type.
  52. #![allow(clippy::too_many_arguments)]
  53. mod proposal_types;
  54. #[cfg(test)]
  55. mod tests;
  56. use frame_support::dispatch::DispatchResult;
  57. use frame_support::traits::{Currency, Get};
  58. use frame_support::{decl_error, decl_module, decl_storage, ensure, print};
  59. use frame_system::ensure_root;
  60. use sp_arithmetic::traits::Zero;
  61. use sp_std::clone::Clone;
  62. use sp_std::str::from_utf8;
  63. use sp_std::vec::Vec;
  64. pub use crate::proposal_types::{
  65. AddOpeningParameters, FillOpeningParameters, TerminateRoleParameters,
  66. };
  67. use common::origin::ActorOriginValidator;
  68. use common::working_group::WorkingGroup;
  69. pub use proposal_types::{ProposalDetails, ProposalDetailsOf, ProposalEncoder};
  70. use proposals_discussion::ThreadMode;
  71. use proposals_engine::{
  72. BalanceOf, ProposalCreationParameters, ProposalObserver, ProposalParameters,
  73. };
  74. use working_group::Penalty;
  75. // 'Set working group budget capacity' proposal limit
  76. const WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE: u32 = 5_000_000;
  77. // Max allowed value for 'spending' proposal
  78. const MAX_SPENDING_PROPOSAL_VALUE: u32 = 5_000_000_u32;
  79. // Max validator count for the 'set validator count' proposal
  80. const MAX_VALIDATOR_COUNT: u32 = 100;
  81. // Data container struct to fix linter warning 'too many arguments for the function' for the
  82. // create_proposal() function.
  83. struct CreateProposalParameters<T: Trait> {
  84. pub origin: T::Origin,
  85. pub member_id: MemberId<T>,
  86. pub title: Vec<u8>,
  87. pub description: Vec<u8>,
  88. pub staking_account_id: Option<T::AccountId>,
  89. pub proposal_code: Vec<u8>,
  90. pub proposal_parameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>,
  91. pub proposal_details: ProposalDetailsOf<T>,
  92. pub exact_execution_block: Option<T::BlockNumber>,
  93. }
  94. /// 'Proposals codex' substrate module Trait
  95. pub trait Trait:
  96. frame_system::Trait
  97. + proposals_engine::Trait
  98. + proposals_discussion::Trait
  99. + membership::Trait
  100. + governance::election::Trait
  101. + hiring::Trait
  102. + staking::Trait
  103. {
  104. /// Defines max allowed text proposal length.
  105. type TextProposalMaxLength: Get<u32>;
  106. /// Defines max wasm code length of the runtime upgrade proposal.
  107. type RuntimeUpgradeWasmProposalMaxLength: Get<u32>;
  108. /// Validates member id and origin combination.
  109. type MembershipOriginValidator: ActorOriginValidator<
  110. Self::Origin,
  111. MemberId<Self>,
  112. Self::AccountId,
  113. >;
  114. /// Encodes the proposal usint its details.
  115. type ProposalEncoder: ProposalEncoder<Self>;
  116. /// 'Set validator count' proposal parameters.
  117. type SetValidatorCountProposalParameters: Get<
  118. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  119. >;
  120. /// 'Runtime upgrade' proposal parameters.
  121. type RuntimeUpgradeProposalParameters: Get<
  122. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  123. >;
  124. /// 'Text' proposal parameters.
  125. type TextProposalParameters: Get<ProposalParameters<Self::BlockNumber, BalanceOf<Self>>>;
  126. /// 'Spending' proposal parameters.
  127. type SpendingProposalParameters: Get<ProposalParameters<Self::BlockNumber, BalanceOf<Self>>>;
  128. /// 'Add working group opening' proposal parameters.
  129. type AddWorkingGroupOpeningProposalParameters: Get<
  130. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  131. >;
  132. /// 'Begin review working group applications' proposal parameters.
  133. type BeginReviewWorkingGroupApplicationsProposalParameters: Get<
  134. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  135. >;
  136. /// 'Fill working group opening' proposal parameters.
  137. type FillWorkingGroupOpeningProposalParameters: Get<
  138. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  139. >;
  140. /// 'Set working group budget capacity' proposal parameters.
  141. type SetWorkingGroupBudgetCapacityProposalParameters: Get<
  142. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  143. >;
  144. /// 'Decrease working group leader stake' proposal parameters.
  145. type DecreaseWorkingGroupLeaderStakeProposalParameters: Get<
  146. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  147. >;
  148. /// 'Slash working group leader stake' proposal parameters.
  149. type SlashWorkingGroupLeaderStakeProposalParameters: Get<
  150. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  151. >;
  152. /// 'Set working group leader reward' proposal parameters.
  153. type SetWorkingGroupLeaderRewardProposalParameters: Get<
  154. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  155. >;
  156. /// 'Terminate working group leader role' proposal parameters.
  157. type TerminateWorkingGroupLeaderRoleProposalParameters: Get<
  158. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  159. >;
  160. /// 'Amend constitution' proposal parameters.
  161. type AmendConstitutionProposalParameters: Get<
  162. ProposalParameters<Self::BlockNumber, BalanceOf<Self>>,
  163. >;
  164. }
  165. /// Balance alias for GovernanceCurrency from `common` module. TODO: replace with BalanceOf
  166. pub type BalanceOfGovernanceCurrency<T> =
  167. <<T as common::currency::GovernanceCurrency>::Currency as Currency<
  168. <T as frame_system::Trait>::AccountId,
  169. >>::Balance;
  170. /// Balance alias for token mint balance from `token mint` module. TODO: replace with BalanceOf
  171. pub type BalanceOfMint<T> =
  172. <<T as minting::Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
  173. type MemberId<T> = <T as membership::Trait>::MemberId;
  174. decl_error! {
  175. /// Codex module predefined errors
  176. pub enum Error for Module<T: Trait> {
  177. /// The size of the provided text for text proposal exceeded the limit
  178. TextProposalSizeExceeded,
  179. /// Provided text for text proposal is empty
  180. TextProposalIsEmpty,
  181. /// The size of the provided WASM code for the runtime upgrade proposal exceeded the limit
  182. RuntimeProposalSizeExceeded,
  183. /// Provided WASM code for the runtime upgrade proposal is empty
  184. RuntimeProposalIsEmpty,
  185. /// Invalid balance value for the spending proposal
  186. InvalidSpendingProposalBalance,
  187. /// Invalid validator count for the 'set validator count' proposal
  188. InvalidValidatorCount,
  189. /// Require root origin in extrinsics
  190. RequireRootOrigin,
  191. /// Invalid council election parameter - council_size
  192. InvalidCouncilElectionParameterCouncilSize,
  193. /// Invalid council election parameter - candidacy-limit
  194. InvalidCouncilElectionParameterCandidacyLimit,
  195. /// Invalid council election parameter - min-voting_stake
  196. InvalidCouncilElectionParameterMinVotingStake,
  197. /// Invalid council election parameter - new_term_duration
  198. InvalidCouncilElectionParameterNewTermDuration,
  199. /// Invalid council election parameter - min_council_stake
  200. InvalidCouncilElectionParameterMinCouncilStake,
  201. /// Invalid council election parameter - revealing_period
  202. InvalidCouncilElectionParameterRevealingPeriod,
  203. /// Invalid council election parameter - voting_period
  204. InvalidCouncilElectionParameterVotingPeriod,
  205. /// Invalid council election parameter - announcing_period
  206. InvalidCouncilElectionParameterAnnouncingPeriod,
  207. /// Invalid working group budget capacity parameter
  208. InvalidWorkingGroupBudgetCapacity,
  209. /// Invalid 'set lead proposal' parameter - proposed lead cannot be a councilor
  210. InvalidSetLeadParameterCannotBeCouncilor,
  211. /// Invalid 'slash stake proposal' parameter - cannot slash by zero balance.
  212. SlashingStakeIsZero,
  213. /// Invalid 'decrease stake proposal' parameter - cannot decrease by zero balance.
  214. DecreasingStakeIsZero,
  215. }
  216. }
  217. // Storage for the proposals codex module
  218. decl_storage! {
  219. pub trait Store for Module<T: Trait> as ProposalCodex {
  220. /// Map proposal id to its discussion thread id
  221. pub ThreadIdByProposalId get(fn thread_id_by_proposal_id):
  222. map hasher(blake2_128_concat) T::ProposalId => T::ThreadId;
  223. /// Map proposal id to proposal details
  224. pub ProposalDetailsByProposalId: map hasher(blake2_128_concat) T::ProposalId => ProposalDetailsOf<T>;
  225. }
  226. }
  227. decl_module! {
  228. /// Proposal codex substrate module Call
  229. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  230. /// Predefined errors
  231. type Error = Error<T>;
  232. /// Exports 'Set validator count' proposal parameters.
  233. const SetValidatorCountProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  234. = T::SetValidatorCountProposalParameters::get();
  235. /// Exports 'Runtime upgrade' proposal parameters.
  236. const RuntimeUpgradeProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  237. = T::RuntimeUpgradeProposalParameters::get();
  238. /// Exports 'Text' proposal parameters.
  239. const TextProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  240. = T::TextProposalParameters::get();
  241. /// Exports 'Spending' proposal parameters.
  242. const SpendingProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  243. = T::SpendingProposalParameters::get();
  244. /// Exports 'Add working group opening' proposal parameters.
  245. const AddWorkingGroupOpeningProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  246. = T::AddWorkingGroupOpeningProposalParameters::get();
  247. /// Exports 'Begin review working group applications' proposal parameters.
  248. const BeginReviewWorkingGroupApplicationsProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  249. = T::BeginReviewWorkingGroupApplicationsProposalParameters::get();
  250. /// Exports 'Fill working group opening' proposal parameters.
  251. const FillWorkingGroupOpeningProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  252. = T::FillWorkingGroupOpeningProposalParameters::get();
  253. /// Exports 'Set working group budget capacity' proposal parameters.
  254. const SetWorkingGroupBudgetCapacityProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  255. = T::SetWorkingGroupBudgetCapacityProposalParameters::get();
  256. /// Exports 'Decrease working group leader stake' proposal parameters.
  257. const DecreaseWorkingGroupLeaderStakeProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  258. = T::DecreaseWorkingGroupLeaderStakeProposalParameters::get();
  259. /// Exports 'Slash working group leader stake' proposal parameters.
  260. const SlashWorkingGroupLeaderStakeProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  261. = T::SlashWorkingGroupLeaderStakeProposalParameters::get();
  262. /// Exports 'Set working group leader reward' proposal parameters.
  263. const SetWorkingGroupLeaderRewardProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  264. = T::SetWorkingGroupLeaderRewardProposalParameters::get();
  265. /// Exports 'Terminate working group leader role' proposal parameters.
  266. const TerminateWorkingGroupLeaderRoleProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  267. = T::TerminateWorkingGroupLeaderRoleProposalParameters::get();
  268. /// Exports 'Amend constitution' proposal parameters.
  269. const AmendConstitutionProposalParameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>
  270. = T::AmendConstitutionProposalParameters::get();
  271. /// Exports max allowed text proposal length const.
  272. const TextProposalMaxLength: u32 = T::TextProposalMaxLength::get();
  273. /// Exports max wasm code length of the runtime upgrade proposal const.
  274. const RuntimeUpgradeWasmProposalMaxLength: u32 = T::RuntimeUpgradeWasmProposalMaxLength::get();
  275. /// Create 'Text (signal)' proposal type.
  276. #[weight = 10_000_000] // TODO: adjust weight
  277. pub fn create_text_proposal(
  278. origin,
  279. member_id: MemberId<T>,
  280. title: Vec<u8>,
  281. description: Vec<u8>,
  282. staking_account_id: Option<T::AccountId>,
  283. text: Vec<u8>,
  284. exact_execution_block: Option<T::BlockNumber>,
  285. ) {
  286. ensure!(!text.is_empty(), Error::<T>::TextProposalIsEmpty);
  287. ensure!(text.len() as u32 <= T::TextProposalMaxLength::get(),
  288. Error::<T>::TextProposalSizeExceeded);
  289. let proposal_details = ProposalDetails::Text(text);
  290. let params = CreateProposalParameters{
  291. origin,
  292. member_id,
  293. title,
  294. description,
  295. staking_account_id,
  296. proposal_details: proposal_details.clone(),
  297. proposal_parameters: T::TextProposalParameters::get(),
  298. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  299. exact_execution_block
  300. };
  301. Self::create_proposal(params)?;
  302. }
  303. /// Create 'Runtime upgrade' proposal type. Runtime upgrade can be initiated only by
  304. /// members from the hardcoded list `RuntimeUpgradeProposalAllowedProposers`
  305. #[weight = 10_000_000] // TODO: adjust weight
  306. pub fn create_runtime_upgrade_proposal(
  307. origin,
  308. member_id: MemberId<T>,
  309. title: Vec<u8>,
  310. description: Vec<u8>,
  311. staking_account_id: Option<T::AccountId>,
  312. wasm: Vec<u8>,
  313. exact_execution_block: Option<T::BlockNumber>,
  314. ) {
  315. ensure!(!wasm.is_empty(), Error::<T>::RuntimeProposalIsEmpty);
  316. ensure!(wasm.len() as u32 <= T::RuntimeUpgradeWasmProposalMaxLength::get(),
  317. Error::<T>::RuntimeProposalSizeExceeded);
  318. let proposal_details = ProposalDetails::RuntimeUpgrade(wasm);
  319. let params = CreateProposalParameters{
  320. origin,
  321. member_id,
  322. title,
  323. description,
  324. staking_account_id,
  325. proposal_details: proposal_details.clone(),
  326. proposal_parameters: T::RuntimeUpgradeProposalParameters::get(),
  327. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  328. exact_execution_block,
  329. };
  330. Self::create_proposal(params)?;
  331. }
  332. /// Create 'Spending' proposal type.
  333. /// This proposal uses `spend_from_council_mint()` extrinsic from the `governance::council` module.
  334. #[weight = 10_000_000] // TODO: adjust weight
  335. pub fn create_spending_proposal(
  336. origin,
  337. member_id: MemberId<T>,
  338. title: Vec<u8>,
  339. description: Vec<u8>,
  340. staking_account_id: Option<T::AccountId>,
  341. balance: BalanceOfMint<T>,
  342. destination: T::AccountId,
  343. exact_execution_block: Option<T::BlockNumber>,
  344. ) {
  345. ensure!(balance != BalanceOfMint::<T>::zero(), Error::<T>::InvalidSpendingProposalBalance);
  346. ensure!(
  347. balance <= <BalanceOfMint<T>>::from(MAX_SPENDING_PROPOSAL_VALUE),
  348. Error::<T>::InvalidSpendingProposalBalance
  349. );
  350. let proposal_details = ProposalDetails::Spending(balance, destination);
  351. let params = CreateProposalParameters{
  352. origin,
  353. member_id,
  354. title,
  355. description,
  356. staking_account_id,
  357. proposal_details: proposal_details.clone(),
  358. proposal_parameters: T::SpendingProposalParameters::get(),
  359. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  360. exact_execution_block,
  361. };
  362. Self::create_proposal(params)?;
  363. }
  364. /// Create 'Evict storage provider' proposal type.
  365. /// This proposal uses `set_validator_count()` extrinsic from the Substrate `staking` module.
  366. #[weight = 10_000_000] // TODO: adjust weight
  367. pub fn create_set_validator_count_proposal(
  368. origin,
  369. member_id: MemberId<T>,
  370. title: Vec<u8>,
  371. description: Vec<u8>,
  372. staking_account_id: Option<T::AccountId>,
  373. new_validator_count: u32,
  374. exact_execution_block: Option<T::BlockNumber>,
  375. ) {
  376. ensure!(
  377. new_validator_count >= <staking::Module<T>>::minimum_validator_count(),
  378. Error::<T>::InvalidValidatorCount
  379. );
  380. ensure!(
  381. new_validator_count <= MAX_VALIDATOR_COUNT,
  382. Error::<T>::InvalidValidatorCount
  383. );
  384. let proposal_details = ProposalDetails::SetValidatorCount(new_validator_count);
  385. let params = CreateProposalParameters{
  386. origin,
  387. member_id,
  388. title,
  389. description,
  390. staking_account_id,
  391. proposal_details: proposal_details.clone(),
  392. proposal_parameters: T::SetValidatorCountProposalParameters::get(),
  393. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  394. exact_execution_block,
  395. };
  396. Self::create_proposal(params)?;
  397. }
  398. /// Create 'Add working group leader opening' proposal type.
  399. /// This proposal uses `add_opening()` extrinsic from the Joystream `working group` module.
  400. #[weight = 10_000_000] // TODO: adjust weight
  401. pub fn create_add_working_group_leader_opening_proposal(
  402. origin,
  403. member_id: MemberId<T>,
  404. title: Vec<u8>,
  405. description: Vec<u8>,
  406. staking_account_id: Option<T::AccountId>,
  407. add_opening_parameters: AddOpeningParameters<T::BlockNumber, BalanceOfGovernanceCurrency<T>>,
  408. exact_execution_block: Option<T::BlockNumber>,
  409. ) {
  410. let proposal_details = ProposalDetails::AddWorkingGroupLeaderOpening(add_opening_parameters);
  411. let params = CreateProposalParameters{
  412. origin,
  413. member_id,
  414. title,
  415. description,
  416. staking_account_id,
  417. proposal_details: proposal_details.clone(),
  418. proposal_parameters: T::AddWorkingGroupOpeningProposalParameters::get(),
  419. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  420. exact_execution_block,
  421. };
  422. Self::create_proposal(params)?;
  423. }
  424. /// Create 'Fill working group leader opening' proposal type.
  425. /// This proposal uses `fill_opening()` extrinsic from the Joystream `working group` module.
  426. #[weight = 10_000_000] // TODO: adjust weight
  427. pub fn create_fill_working_group_leader_opening_proposal(
  428. origin,
  429. member_id: MemberId<T>,
  430. title: Vec<u8>,
  431. description: Vec<u8>,
  432. staking_account_id: Option<T::AccountId>,
  433. fill_opening_parameters: FillOpeningParameters,
  434. exact_execution_block: Option<T::BlockNumber>,
  435. ) {
  436. let proposal_details = ProposalDetails::FillWorkingGroupLeaderOpening(fill_opening_parameters);
  437. let params = CreateProposalParameters{
  438. origin,
  439. member_id,
  440. title,
  441. description,
  442. staking_account_id,
  443. proposal_details: proposal_details.clone(),
  444. proposal_parameters: T::FillWorkingGroupOpeningProposalParameters::get(),
  445. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  446. exact_execution_block,
  447. };
  448. Self::create_proposal(params)?;
  449. }
  450. /// Create 'Set working group budget capacity' proposal type.
  451. /// This proposal uses `set_mint_capacity()` extrinsic from the `working-group` module.
  452. #[weight = 10_000_000] // TODO: adjust weight
  453. pub fn create_set_working_group_budget_capacity_proposal(
  454. origin,
  455. member_id: MemberId<T>,
  456. title: Vec<u8>,
  457. description: Vec<u8>,
  458. staking_account_id: Option<T::AccountId>,
  459. mint_balance: BalanceOfMint<T>,
  460. working_group: WorkingGroup,
  461. exact_execution_block: Option<T::BlockNumber>,
  462. ) {
  463. ensure!(
  464. mint_balance <= <BalanceOfMint<T>>::from(WORKING_GROUP_BUDGET_CAPACITY_MAX_VALUE),
  465. Error::<T>::InvalidWorkingGroupBudgetCapacity
  466. );
  467. let proposal_details = ProposalDetails::SetWorkingGroupBudgetCapacity(mint_balance, working_group);
  468. let params = CreateProposalParameters{
  469. origin,
  470. member_id,
  471. title,
  472. description,
  473. staking_account_id,
  474. proposal_details: proposal_details.clone(),
  475. proposal_parameters: T::SetWorkingGroupBudgetCapacityProposalParameters::get(),
  476. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  477. exact_execution_block,
  478. };
  479. Self::create_proposal(params)?;
  480. }
  481. /// Create 'decrease working group leader stake' proposal type.
  482. /// This proposal uses `decrease_stake()` extrinsic from the `working-group` module.
  483. #[weight = 10_000_000] // TODO: adjust weight
  484. pub fn create_decrease_working_group_leader_stake_proposal(
  485. origin,
  486. member_id: MemberId<T>,
  487. title: Vec<u8>,
  488. description: Vec<u8>,
  489. staking_account_id: Option<T::AccountId>,
  490. worker_id: working_group::WorkerId<T>,
  491. decreasing_stake: BalanceOf<T>,
  492. working_group: WorkingGroup,
  493. exact_execution_block: Option<T::BlockNumber>,
  494. ) {
  495. ensure!(decreasing_stake != Zero::zero(), Error::<T>::DecreasingStakeIsZero);
  496. let proposal_details = ProposalDetails::DecreaseWorkingGroupLeaderStake(
  497. worker_id,
  498. decreasing_stake,
  499. working_group
  500. );
  501. let params = CreateProposalParameters{
  502. origin,
  503. member_id,
  504. title,
  505. description,
  506. staking_account_id,
  507. proposal_details: proposal_details.clone(),
  508. proposal_parameters: T::DecreaseWorkingGroupLeaderStakeProposalParameters::get(),
  509. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  510. exact_execution_block,
  511. };
  512. Self::create_proposal(params)?;
  513. }
  514. /// Create 'slash working group leader stake' proposal type.
  515. /// This proposal uses `slash_stake()` extrinsic from the `working-group` module.
  516. #[weight = 10_000_000] // TODO: adjust weight
  517. pub fn create_slash_working_group_leader_stake_proposal(
  518. origin,
  519. member_id: MemberId<T>,
  520. title: Vec<u8>,
  521. description: Vec<u8>,
  522. staking_account_id: Option<T::AccountId>,
  523. worker_id: working_group::WorkerId<T>,
  524. penalty: Penalty<BalanceOf<T>>,
  525. working_group: WorkingGroup,
  526. exact_execution_block: Option<T::BlockNumber>,
  527. ) {
  528. ensure!(penalty.slashing_amount != Zero::zero(), Error::<T>::SlashingStakeIsZero);
  529. let proposal_details = ProposalDetails::SlashWorkingGroupLeaderStake(
  530. worker_id,
  531. penalty,
  532. working_group
  533. );
  534. let params = CreateProposalParameters{
  535. origin,
  536. member_id,
  537. title,
  538. description,
  539. staking_account_id,
  540. proposal_details: proposal_details.clone(),
  541. proposal_parameters: T::SlashWorkingGroupLeaderStakeProposalParameters::get(),
  542. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  543. exact_execution_block,
  544. };
  545. Self::create_proposal(params)?;
  546. }
  547. /// Create 'set working group leader reward' proposal type.
  548. /// This proposal uses `update_reward_amount()` extrinsic from the `working-group` module.
  549. #[weight = 10_000_000] // TODO: adjust weight
  550. pub fn create_set_working_group_leader_reward_proposal(
  551. origin,
  552. member_id: MemberId<T>,
  553. title: Vec<u8>,
  554. description: Vec<u8>,
  555. staking_account_id: Option<T::AccountId>,
  556. worker_id: working_group::WorkerId<T>,
  557. reward_amount: Option<BalanceOfMint<T>>,
  558. working_group: WorkingGroup,
  559. exact_execution_block: Option<T::BlockNumber>,
  560. ) {
  561. let proposal_details = ProposalDetails::SetWorkingGroupLeaderReward(
  562. worker_id,
  563. reward_amount,
  564. working_group
  565. );
  566. let params = CreateProposalParameters{
  567. origin,
  568. member_id,
  569. title,
  570. description,
  571. staking_account_id,
  572. proposal_details: proposal_details.clone(),
  573. proposal_parameters: T::SetWorkingGroupLeaderRewardProposalParameters::get(),
  574. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  575. exact_execution_block,
  576. };
  577. Self::create_proposal(params)?;
  578. }
  579. /// Create 'terminate working group leader role' proposal type.
  580. /// This proposal uses `terminate_role()` extrinsic from the `working-group` module.
  581. #[weight = 10_000_000] // TODO: adjust weight
  582. pub fn create_terminate_working_group_leader_role_proposal(
  583. origin,
  584. member_id: MemberId<T>,
  585. title: Vec<u8>,
  586. description: Vec<u8>,
  587. staking_account_id: Option<T::AccountId>,
  588. terminate_role_parameters: TerminateRoleParameters<working_group::WorkerId<T>, BalanceOf<T>>,
  589. exact_execution_block: Option<T::BlockNumber>,
  590. ) {
  591. let proposal_details = ProposalDetails::TerminateWorkingGroupLeaderRole(terminate_role_parameters);
  592. let params = CreateProposalParameters{
  593. origin,
  594. member_id,
  595. title,
  596. description,
  597. staking_account_id,
  598. proposal_details: proposal_details.clone(),
  599. proposal_parameters: T::TerminateWorkingGroupLeaderRoleProposalParameters::get(),
  600. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  601. exact_execution_block,
  602. };
  603. Self::create_proposal(params)?;
  604. }
  605. /// Create 'amend constitution' proposal type.
  606. /// This proposal uses `amend_constitution()` extrinsic from the `constitution` module.
  607. #[weight = 10_000_000] // TODO: adjust weight
  608. pub fn create_amend_constitution_proposal(
  609. origin,
  610. member_id: MemberId<T>,
  611. title: Vec<u8>,
  612. description: Vec<u8>,
  613. staking_account_id: Option<T::AccountId>,
  614. constitution_text: Vec<u8>,
  615. exact_execution_block: Option<T::BlockNumber>,
  616. ) {
  617. let proposal_details = ProposalDetails::AmendConstitution(constitution_text);
  618. let params = CreateProposalParameters{
  619. origin,
  620. member_id,
  621. title,
  622. description,
  623. staking_account_id,
  624. proposal_details: proposal_details.clone(),
  625. proposal_parameters: T::AmendConstitutionProposalParameters::get(),
  626. proposal_code: T::ProposalEncoder::encode_proposal(proposal_details),
  627. exact_execution_block,
  628. };
  629. Self::create_proposal(params)?;
  630. }
  631. // *************** Extrinsic to execute
  632. /// Text proposal extrinsic. Should be used as callable object to pass to the `engine` module.
  633. #[weight = 10_000_000] // TODO: adjust weight
  634. pub fn execute_text_proposal(
  635. origin,
  636. text: Vec<u8>,
  637. ) {
  638. ensure_root(origin)?;
  639. print("Text proposal: ");
  640. let text_string_result = from_utf8(text.as_slice());
  641. if let Ok(text_string) = text_string_result{
  642. print(text_string);
  643. }
  644. }
  645. /// Runtime upgrade proposal extrinsic.
  646. /// Should be used as callable object to pass to the `engine` module.
  647. #[weight = 10_000_000] // TODO: adjust weight
  648. pub fn execute_runtime_upgrade_proposal(
  649. origin,
  650. wasm: Vec<u8>,
  651. ) {
  652. ensure_root(origin.clone())?;
  653. print("Runtime upgrade proposal execution started.");
  654. <frame_system::Module<T>>::set_code(origin, wasm)?;
  655. print("Runtime upgrade proposal execution finished.");
  656. }
  657. }
  658. }
  659. impl<T: Trait> Module<T> {
  660. // Generic template proposal builder
  661. fn create_proposal(params: CreateProposalParameters<T>) -> DispatchResult {
  662. let account_id =
  663. T::MembershipOriginValidator::ensure_actor_origin(params.origin, params.member_id)?;
  664. <proposals_engine::Module<T>>::ensure_create_proposal_parameters_are_valid(
  665. &params.proposal_parameters,
  666. &params.title,
  667. &params.description,
  668. params.staking_account_id.clone(),
  669. params.exact_execution_block,
  670. )?;
  671. let initial_thread_mode = ThreadMode::Open;
  672. <proposals_discussion::Module<T>>::ensure_can_create_thread(&initial_thread_mode)?;
  673. let discussion_thread_id = <proposals_discussion::Module<T>>::create_thread(
  674. params.member_id,
  675. initial_thread_mode,
  676. )?;
  677. let proposal_creation_params = ProposalCreationParameters {
  678. account_id,
  679. proposer_id: params.member_id,
  680. proposal_parameters: params.proposal_parameters,
  681. title: params.title,
  682. description: params.description,
  683. staking_account_id: params.staking_account_id,
  684. encoded_dispatchable_call_code: params.proposal_code,
  685. exact_execution_block: params.exact_execution_block,
  686. };
  687. let proposal_id = <proposals_engine::Module<T>>::create_proposal(proposal_creation_params)?;
  688. <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
  689. <ProposalDetailsByProposalId<T>>::insert(proposal_id, params.proposal_details);
  690. Ok(())
  691. }
  692. }
  693. impl<T: Trait> ProposalObserver<T> for Module<T> {
  694. fn proposal_removed(proposal_id: &<T as proposals_engine::Trait>::ProposalId) {
  695. <ThreadIdByProposalId<T>>::remove(proposal_id);
  696. <ProposalDetailsByProposalId<T>>::remove(proposal_id);
  697. let thread_id = Self::thread_id_by_proposal_id(proposal_id);
  698. proposals_discussion::ThreadById::<T>::remove(thread_id);
  699. proposals_discussion::PostThreadIdByPostId::<T>::remove_prefix(thread_id);
  700. }
  701. }