lib.rs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. //! # Proposals codex module
  2. //! Proposals `codex` module for the Joystream platform. Version 2.
  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. //! ### Supported extrinsics (proposal types)
  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_election_parameters_proposal](./struct.Module.html#method.create_set_election_parameters_proposal)
  19. //! - [create_set_content_working_group_mint_capacity_proposal](./struct.Module.html#method.create_set_content_working_group_mint_capacity_proposal)
  20. //! - [create_spending_proposal](./struct.Module.html#method.create_spending_proposal)
  21. //! - [create_set_lead_proposal](./struct.Module.html#method.create_set_lead_proposal)
  22. //! - [create_evict_storage_provider_proposal](./struct.Module.html#method.create_evict_storage_provider_proposal)
  23. //! - [create_set_validator_count_proposal](./struct.Module.html#method.create_set_validator_count_proposal)
  24. //! - [create_set_storage_role_parameters_proposal](./struct.Module.html#method.create_set_storage_role_parameters_proposal)
  25. //!
  26. //! ### Proposal implementations of this module
  27. //! - execute_text_proposal - prints the proposal to the log
  28. //! - execute_runtime_upgrade_proposal - sets the runtime code
  29. //!
  30. //! ### Dependencies:
  31. //! - [proposals engine](../substrate_proposals_engine_module/index.html)
  32. //! - [proposals discussion](../substrate_proposals_discussion_module/index.html)
  33. //! - [membership](../substrate_membership_module/index.html)
  34. //! - [governance](../substrate_governance_module/index.html)
  35. //! - [content_working_group](../substrate_content_working_group_module/index.html)
  36. //!
  37. // Ensure we're `no_std` when compiling for Wasm.
  38. #![cfg_attr(not(feature = "std"), no_std)]
  39. // Do not delete! Cannot be uncommented by default, because of Parity decl_module! issue.
  40. // #![warn(missing_docs)]
  41. mod proposal_types;
  42. #[cfg(test)]
  43. mod tests;
  44. use codec::Encode;
  45. use common::origin_validator::ActorOriginValidator;
  46. use governance::election_params::ElectionParameters;
  47. use proposal_engine::ProposalParameters;
  48. use roles::actors::{Role, RoleParameters};
  49. use rstd::clone::Clone;
  50. use rstd::convert::TryInto;
  51. use rstd::prelude::*;
  52. use rstd::str::from_utf8;
  53. use rstd::vec::Vec;
  54. use runtime_io::blake2_256;
  55. use sr_primitives::traits::SaturatedConversion;
  56. use sr_primitives::traits::{One, Zero};
  57. use sr_primitives::Perbill;
  58. use srml_support::dispatch::DispatchResult;
  59. use srml_support::traits::{Currency, Get};
  60. use srml_support::{decl_error, decl_module, decl_storage, ensure, print};
  61. use system::{ensure_root, RawOrigin};
  62. pub use proposal_types::ProposalDetails;
  63. // Percentage of the total token issue as max mint balance value. Shared with spending
  64. // proposal max balance percentage.
  65. const COUNCIL_MINT_MAX_BALANCE_PERCENT: u32 = 2;
  66. /// 'Proposals codex' substrate module Trait
  67. pub trait Trait:
  68. system::Trait
  69. + proposal_engine::Trait
  70. + proposal_discussion::Trait
  71. + membership::members::Trait
  72. + governance::election::Trait
  73. + content_working_group::Trait
  74. + roles::actors::Trait
  75. + staking::Trait
  76. {
  77. /// Defines max allowed text proposal length.
  78. type TextProposalMaxLength: Get<u32>;
  79. /// Defines max wasm code length of the runtime upgrade proposal.
  80. type RuntimeUpgradeWasmProposalMaxLength: Get<u32>;
  81. /// Validates member id and origin combination
  82. type MembershipOriginValidator: ActorOriginValidator<
  83. Self::Origin,
  84. MemberId<Self>,
  85. Self::AccountId,
  86. >;
  87. }
  88. /// Balance alias for `stake` module
  89. pub type BalanceOf<T> =
  90. <<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
  91. /// Currency alias for `stake` module
  92. pub type CurrencyOf<T> = <T as stake::Trait>::Currency;
  93. /// Balance alias for GovernanceCurrency from `common` module. TODO: replace with BalanceOf
  94. pub type BalanceOfGovernanceCurrency<T> =
  95. <<T as common::currency::GovernanceCurrency>::Currency as Currency<
  96. <T as system::Trait>::AccountId,
  97. >>::Balance;
  98. /// Balance alias for token mint balance from `token mint` module. TODO: replace with BalanceOf
  99. pub type BalanceOfMint<T> =
  100. <<T as mint::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
  101. /// Negative imbalance alias for staking
  102. pub type NegativeImbalance<T> =
  103. <<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
  104. type MemberId<T> = <T as membership::members::Trait>::MemberId;
  105. decl_error! {
  106. /// Codex module predefined errors
  107. pub enum Error {
  108. /// The size of the provided text for text proposal exceeded the limit
  109. TextProposalSizeExceeded,
  110. /// Provided text for text proposal is empty
  111. TextProposalIsEmpty,
  112. /// The size of the provided WASM code for the runtime upgrade proposal exceeded the limit
  113. RuntimeProposalSizeExceeded,
  114. /// Provided WASM code for the runtime upgrade proposal is empty
  115. RuntimeProposalIsEmpty,
  116. /// Invalid balance value for the spending proposal
  117. InvalidSpendingProposalBalance,
  118. /// Invalid validator count for the 'set validator count' proposal
  119. InvalidValidatorCount,
  120. /// Require root origin in extrinsics
  121. RequireRootOrigin,
  122. /// Invalid storage role parameter - min_actors
  123. InvalidStorageRoleParameterMinActors,
  124. /// Invalid storage role parameter - max_actors
  125. InvalidStorageRoleParameterMaxActors,
  126. /// Invalid storage role parameter - reward_period
  127. InvalidStorageRoleParameterRewardPeriod,
  128. /// Invalid storage role parameter - bonding_period
  129. InvalidStorageRoleParameterBondingPeriod,
  130. /// Invalid storage role parameter - unbonding_period
  131. InvalidStorageRoleParameterUnbondingPeriod,
  132. /// Invalid storage role parameter - min_service_period
  133. InvalidStorageRoleParameterMinServicePeriod,
  134. /// Invalid storage role parameter - startup_grace_period
  135. InvalidStorageRoleParameterStartupGracePeriod,
  136. /// Invalid council election parameter - council_size
  137. InvalidCouncilElectionParameterCouncilSize,
  138. /// Invalid council election parameter - candidacy-limit
  139. InvalidCouncilElectionParameterCandidacyLimit,
  140. /// Invalid council election parameter - min-voting_stake
  141. InvalidCouncilElectionParameterMinVotingStake,
  142. /// Invalid council election parameter - new_term_duration
  143. InvalidCouncilElectionParameterNewTermDuration,
  144. /// Invalid council election parameter - min_council_stake
  145. InvalidCouncilElectionParameterMinCouncilStake,
  146. /// Invalid council election parameter - revealing_period
  147. InvalidCouncilElectionParameterRevealingPeriod,
  148. /// Invalid council election parameter - voting_period
  149. InvalidCouncilElectionParameterVotingPeriod,
  150. /// Invalid council election parameter - announcing_period
  151. InvalidCouncilElectionParameterAnnouncingPeriod,
  152. /// Invalid council election parameter - min_stake
  153. InvalidStorageRoleParameterMinStake,
  154. /// Invalid council election parameter - reward
  155. InvalidStorageRoleParameterReward,
  156. /// Invalid council election parameter - entry_request_fee
  157. InvalidStorageRoleParameterEntryRequestFee,
  158. /// Invalid working group mint capacity parameter
  159. InvalidStorageWorkingGroupMintCapacity,
  160. /// Invalid 'set lead proposal' parameter - proposed lead cannot be a councilor
  161. InvalidSetLeadParameterCannotBeCouncilor
  162. }
  163. }
  164. impl From<system::Error> for Error {
  165. fn from(error: system::Error) -> Self {
  166. match error {
  167. system::Error::Other(msg) => Error::Other(msg),
  168. system::Error::RequireRootOrigin => Error::RequireRootOrigin,
  169. _ => Error::Other(error.into()),
  170. }
  171. }
  172. }
  173. impl From<proposal_engine::Error> for Error {
  174. fn from(error: proposal_engine::Error) -> Self {
  175. match error {
  176. proposal_engine::Error::Other(msg) => Error::Other(msg),
  177. proposal_engine::Error::RequireRootOrigin => Error::RequireRootOrigin,
  178. _ => Error::Other(error.into()),
  179. }
  180. }
  181. }
  182. impl From<proposal_discussion::Error> for Error {
  183. fn from(error: proposal_discussion::Error) -> Self {
  184. match error {
  185. proposal_discussion::Error::Other(msg) => Error::Other(msg),
  186. proposal_discussion::Error::RequireRootOrigin => Error::RequireRootOrigin,
  187. _ => Error::Other(error.into()),
  188. }
  189. }
  190. }
  191. // Storage for the proposals codex module
  192. decl_storage! {
  193. pub trait Store for Module<T: Trait> as ProposalCodex{
  194. /// Map proposal id to its discussion thread id
  195. pub ThreadIdByProposalId get(fn thread_id_by_proposal_id):
  196. map T::ProposalId => T::ThreadId;
  197. /// Map proposal id to proposal details
  198. pub ProposalDetailsByProposalId get(fn proposal_details_by_proposal_id):
  199. map T::ProposalId => ProposalDetails<
  200. BalanceOfMint<T>,
  201. BalanceOfGovernanceCurrency<T>,
  202. T::BlockNumber,
  203. T::AccountId,
  204. T::MemberId
  205. >;
  206. }
  207. }
  208. decl_module! {
  209. /// Proposal codex substrate module Call
  210. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  211. /// Predefined errors
  212. type Error = Error;
  213. /// Create 'Text (signal)' proposal type.
  214. pub fn create_text_proposal(
  215. origin,
  216. member_id: MemberId<T>,
  217. title: Vec<u8>,
  218. description: Vec<u8>,
  219. stake_balance: Option<BalanceOf<T>>,
  220. text: Vec<u8>,
  221. ) {
  222. ensure!(!text.is_empty(), Error::TextProposalIsEmpty);
  223. ensure!(text.len() as u32 <= T::TextProposalMaxLength::get(),
  224. Error::TextProposalSizeExceeded);
  225. let proposal_parameters = proposal_types::parameters::text_proposal::<T>();
  226. let proposal_code =
  227. <Call<T>>::execute_text_proposal(title.clone(), description.clone(), text.clone());
  228. Self::create_proposal(
  229. origin,
  230. member_id,
  231. title,
  232. description,
  233. stake_balance,
  234. proposal_code.encode(),
  235. proposal_parameters,
  236. ProposalDetails::Text(text),
  237. )?;
  238. }
  239. /// Create 'Runtime upgrade' proposal type. Runtime upgrade can be initiated only by
  240. /// members from the hardcoded list `RuntimeUpgradeProposalAllowedProposers`
  241. pub fn create_runtime_upgrade_proposal(
  242. origin,
  243. member_id: MemberId<T>,
  244. title: Vec<u8>,
  245. description: Vec<u8>,
  246. stake_balance: Option<BalanceOf<T>>,
  247. wasm: Vec<u8>,
  248. ) {
  249. ensure!(!wasm.is_empty(), Error::RuntimeProposalIsEmpty);
  250. ensure!(wasm.len() as u32 <= T::RuntimeUpgradeWasmProposalMaxLength::get(),
  251. Error::RuntimeProposalSizeExceeded);
  252. let wasm_hash = blake2_256(&wasm);
  253. let proposal_code =
  254. <Call<T>>::execute_runtime_upgrade_proposal(title.clone(), description.clone(), wasm);
  255. let proposal_parameters = proposal_types::parameters::runtime_upgrade_proposal::<T>();
  256. Self::create_proposal(
  257. origin,
  258. member_id,
  259. title,
  260. description,
  261. stake_balance,
  262. proposal_code.encode(),
  263. proposal_parameters,
  264. ProposalDetails::RuntimeUpgrade(wasm_hash.to_vec()),
  265. )?;
  266. }
  267. /// Create 'Set election parameters' proposal type. This proposal uses `set_election_parameters()`
  268. /// extrinsic from the `governance::election module`.
  269. pub fn create_set_election_parameters_proposal(
  270. origin,
  271. member_id: MemberId<T>,
  272. title: Vec<u8>,
  273. description: Vec<u8>,
  274. stake_balance: Option<BalanceOf<T>>,
  275. election_parameters: ElectionParameters<BalanceOfGovernanceCurrency<T>, T::BlockNumber>,
  276. ) {
  277. election_parameters.ensure_valid()?;
  278. Self::ensure_council_election_parameters_valid(&election_parameters)?;
  279. let proposal_code =
  280. <governance::election::Call<T>>::set_election_parameters(election_parameters.clone());
  281. let proposal_parameters =
  282. proposal_types::parameters::set_election_parameters_proposal::<T>();
  283. Self::create_proposal(
  284. origin,
  285. member_id,
  286. title,
  287. description,
  288. stake_balance,
  289. proposal_code.encode(),
  290. proposal_parameters,
  291. ProposalDetails::SetElectionParameters(election_parameters),
  292. )?;
  293. }
  294. /// Create 'Set content working group mint capacity' proposal type.
  295. /// This proposal uses `set_mint_capacity()` extrinsic from the `content-working-group` module.
  296. pub fn create_set_content_working_group_mint_capacity_proposal(
  297. origin,
  298. member_id: MemberId<T>,
  299. title: Vec<u8>,
  300. description: Vec<u8>,
  301. stake_balance: Option<BalanceOf<T>>,
  302. mint_balance: BalanceOfMint<T>,
  303. ) {
  304. let max_mint_capacity: u32 = get_required_stake_by_fraction::<T>(1, 100)
  305. .try_into()
  306. .unwrap_or_default() as u32;
  307. ensure!(
  308. mint_balance < <BalanceOfMint<T>>::from(max_mint_capacity),
  309. Error::InvalidStorageWorkingGroupMintCapacity
  310. );
  311. let proposal_code =
  312. <content_working_group::Call<T>>::set_mint_capacity(mint_balance.clone());
  313. let proposal_parameters =
  314. proposal_types::parameters::set_content_working_group_mint_capacity_proposal::<T>();
  315. Self::create_proposal(
  316. origin,
  317. member_id,
  318. title,
  319. description,
  320. stake_balance,
  321. proposal_code.encode(),
  322. proposal_parameters,
  323. ProposalDetails::SetContentWorkingGroupMintCapacity(mint_balance),
  324. )?;
  325. }
  326. /// Create 'Spending' proposal type.
  327. /// This proposal uses `spend_from_council_mint()` extrinsic from the `governance::council` module.
  328. pub fn create_spending_proposal(
  329. origin,
  330. member_id: MemberId<T>,
  331. title: Vec<u8>,
  332. description: Vec<u8>,
  333. stake_balance: Option<BalanceOf<T>>,
  334. balance: BalanceOfMint<T>,
  335. destination: T::AccountId,
  336. ) {
  337. ensure!(balance != BalanceOfMint::<T>::zero(), Error::InvalidSpendingProposalBalance);
  338. let max_balance: u32 = get_required_stake_by_fraction::<T>(
  339. COUNCIL_MINT_MAX_BALANCE_PERCENT,
  340. 100
  341. )
  342. .try_into()
  343. .unwrap_or_default() as u32;
  344. ensure!(
  345. balance < <BalanceOfMint<T>>::from(max_balance),
  346. Error::InvalidSpendingProposalBalance
  347. );
  348. let proposal_code = <governance::council::Call<T>>::spend_from_council_mint(
  349. balance.clone(),
  350. destination.clone()
  351. );
  352. let proposal_parameters =
  353. proposal_types::parameters::spending_proposal::<T>();
  354. Self::create_proposal(
  355. origin,
  356. member_id,
  357. title,
  358. description,
  359. stake_balance,
  360. proposal_code.encode(),
  361. proposal_parameters,
  362. ProposalDetails::Spending(balance, destination),
  363. )?;
  364. }
  365. /// Create 'Set lead' proposal type.
  366. /// This proposal uses `replace_lead()` extrinsic from the `content_working_group` module.
  367. pub fn create_set_lead_proposal(
  368. origin,
  369. member_id: MemberId<T>,
  370. title: Vec<u8>,
  371. description: Vec<u8>,
  372. stake_balance: Option<BalanceOf<T>>,
  373. new_lead: Option<(T::MemberId, T::AccountId)>
  374. ) {
  375. if let Some(lead) = new_lead.clone() {
  376. let account_id = lead.1;
  377. ensure!(
  378. !<governance::council::Module<T>>::is_councilor(&account_id),
  379. Error::InvalidSetLeadParameterCannotBeCouncilor
  380. );
  381. }
  382. let proposal_code =
  383. <content_working_group::Call<T>>::replace_lead(new_lead.clone());
  384. let proposal_parameters =
  385. proposal_types::parameters::set_lead_proposal::<T>();
  386. Self::create_proposal(
  387. origin,
  388. member_id,
  389. title,
  390. description,
  391. stake_balance,
  392. proposal_code.encode(),
  393. proposal_parameters,
  394. ProposalDetails::SetLead(new_lead),
  395. )?;
  396. }
  397. /// Create 'Evict storage provider' proposal type.
  398. /// This proposal uses `remove_actor()` extrinsic from the `roles::actors` module.
  399. pub fn create_evict_storage_provider_proposal(
  400. origin,
  401. member_id: MemberId<T>,
  402. title: Vec<u8>,
  403. description: Vec<u8>,
  404. stake_balance: Option<BalanceOf<T>>,
  405. actor_account: T::AccountId,
  406. ) {
  407. let proposal_code =
  408. <roles::actors::Call<T>>::remove_actor(actor_account.clone());
  409. let proposal_parameters =
  410. proposal_types::parameters::evict_storage_provider_proposal::<T>();
  411. Self::create_proposal(
  412. origin,
  413. member_id,
  414. title,
  415. description,
  416. stake_balance,
  417. proposal_code.encode(),
  418. proposal_parameters,
  419. ProposalDetails::EvictStorageProvider(actor_account),
  420. )?;
  421. }
  422. /// Create 'Evict storage provider' proposal type.
  423. /// This proposal uses `set_validator_count()` extrinsic from the Substrate `staking` module.
  424. pub fn create_set_validator_count_proposal(
  425. origin,
  426. member_id: MemberId<T>,
  427. title: Vec<u8>,
  428. description: Vec<u8>,
  429. stake_balance: Option<BalanceOf<T>>,
  430. new_validator_count: u32,
  431. ) {
  432. ensure!(
  433. new_validator_count >= <staking::Module<T>>::minimum_validator_count(),
  434. Error::InvalidValidatorCount
  435. );
  436. ensure!(
  437. new_validator_count <= 1000, // max validator count
  438. Error::InvalidValidatorCount
  439. );
  440. let proposal_code =
  441. <staking::Call<T>>::set_validator_count(new_validator_count);
  442. let proposal_parameters =
  443. proposal_types::parameters::set_validator_count_proposal::<T>();
  444. Self::create_proposal(
  445. origin,
  446. member_id,
  447. title,
  448. description,
  449. stake_balance,
  450. proposal_code.encode(),
  451. proposal_parameters,
  452. ProposalDetails::SetValidatorCount(new_validator_count),
  453. )?;
  454. }
  455. /// Create 'Set storage roles parameters' proposal type.
  456. /// This proposal uses `set_role_parameters()` extrinsic from the Substrate `roles::actors` module.
  457. pub fn create_set_storage_role_parameters_proposal(
  458. origin,
  459. member_id: MemberId<T>,
  460. title: Vec<u8>,
  461. description: Vec<u8>,
  462. stake_balance: Option<BalanceOf<T>>,
  463. role_parameters: RoleParameters<BalanceOfGovernanceCurrency<T>, T::BlockNumber>
  464. ) {
  465. Self::ensure_storage_role_parameters_valid(&role_parameters)?;
  466. let proposal_code = <roles::actors::Call<T>>::set_role_parameters(
  467. Role::StorageProvider,
  468. role_parameters.clone()
  469. );
  470. let proposal_parameters =
  471. proposal_types::parameters::set_storage_role_parameters_proposal::<T>();
  472. Self::create_proposal(
  473. origin,
  474. member_id,
  475. title,
  476. description,
  477. stake_balance,
  478. proposal_code.encode(),
  479. proposal_parameters,
  480. ProposalDetails::SetStorageRoleParameters(role_parameters),
  481. )?;
  482. }
  483. // *************** Extrinsic to execute
  484. /// Text proposal extrinsic. Should be used as callable object to pass to the `engine` module.
  485. fn execute_text_proposal(
  486. origin,
  487. title: Vec<u8>,
  488. _description: Vec<u8>,
  489. _text: Vec<u8>,
  490. ) {
  491. ensure_root(origin)?;
  492. print("Text proposal: ");
  493. let title_string_result = from_utf8(title.as_slice());
  494. if let Ok(title_string) = title_string_result{
  495. print(title_string);
  496. }
  497. }
  498. /// Runtime upgrade proposal extrinsic.
  499. /// Should be used as callable object to pass to the `engine` module.
  500. fn execute_runtime_upgrade_proposal(
  501. origin,
  502. title: Vec<u8>,
  503. _description: Vec<u8>,
  504. wasm: Vec<u8>,
  505. ) {
  506. let (cloned_origin1, cloned_origin2) = Self::double_origin(origin);
  507. ensure_root(cloned_origin1)?;
  508. print("Runtime upgrade proposal: ");
  509. let title_string_result = from_utf8(title.as_slice());
  510. if let Ok(title_string) = title_string_result{
  511. print(title_string);
  512. }
  513. <system::Module<T>>::set_code(cloned_origin2, wasm)?;
  514. }
  515. }
  516. }
  517. impl<T: Trait> Module<T> {
  518. // Multiplies the T::Origin.
  519. // In our current substrate version system::Origin doesn't support clone(),
  520. // but it will be supported in latest up-to-date substrate version.
  521. // TODO: delete when T::Origin will support the clone()
  522. fn double_origin(origin: T::Origin) -> (T::Origin, T::Origin) {
  523. let coerced_origin = origin.into().ok().unwrap_or(RawOrigin::None);
  524. let (cloned_origin1, cloned_origin2) = match coerced_origin {
  525. RawOrigin::None => (RawOrigin::None, RawOrigin::None),
  526. RawOrigin::Root => (RawOrigin::Root, RawOrigin::Root),
  527. RawOrigin::Signed(account_id) => (
  528. RawOrigin::Signed(account_id.clone()),
  529. RawOrigin::Signed(account_id),
  530. ),
  531. };
  532. (cloned_origin1.into(), cloned_origin2.into())
  533. }
  534. // Generic template proposal builder
  535. fn create_proposal(
  536. origin: T::Origin,
  537. member_id: MemberId<T>,
  538. title: Vec<u8>,
  539. description: Vec<u8>,
  540. stake_balance: Option<BalanceOf<T>>,
  541. proposal_code: Vec<u8>,
  542. proposal_parameters: ProposalParameters<T::BlockNumber, BalanceOf<T>>,
  543. proposal_details: ProposalDetails<
  544. BalanceOfMint<T>,
  545. BalanceOfGovernanceCurrency<T>,
  546. T::BlockNumber,
  547. T::AccountId,
  548. T::MemberId,
  549. >,
  550. ) -> DispatchResult<Error> {
  551. let account_id =
  552. T::MembershipOriginValidator::ensure_actor_origin(origin, member_id.clone())?;
  553. <proposal_engine::Module<T>>::ensure_create_proposal_parameters_are_valid(
  554. &proposal_parameters,
  555. &title,
  556. &description,
  557. stake_balance,
  558. )?;
  559. <proposal_discussion::Module<T>>::ensure_can_create_thread(member_id.clone(), &title)?;
  560. let discussion_thread_id =
  561. <proposal_discussion::Module<T>>::create_thread(member_id, title.clone())?;
  562. let proposal_id = <proposal_engine::Module<T>>::create_proposal(
  563. account_id,
  564. member_id,
  565. proposal_parameters,
  566. title,
  567. description,
  568. stake_balance,
  569. proposal_code,
  570. )?;
  571. <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
  572. <ProposalDetailsByProposalId<T>>::insert(proposal_id, proposal_details);
  573. Ok(())
  574. }
  575. // validates storage role parameters for the 'Set storage role parameters' proposal
  576. fn ensure_storage_role_parameters_valid(
  577. role_parameters: &RoleParameters<BalanceOfGovernanceCurrency<T>, T::BlockNumber>,
  578. ) -> Result<(), Error> {
  579. ensure!(
  580. role_parameters.min_actors <= 5,
  581. Error::InvalidStorageRoleParameterMinActors
  582. );
  583. ensure!(
  584. role_parameters.max_actors >= 5,
  585. Error::InvalidStorageRoleParameterMaxActors
  586. );
  587. ensure!(
  588. role_parameters.max_actors < 100,
  589. Error::InvalidStorageRoleParameterMaxActors
  590. );
  591. ensure!(
  592. role_parameters.reward_period >= T::BlockNumber::from(600),
  593. Error::InvalidStorageRoleParameterRewardPeriod
  594. );
  595. ensure!(
  596. role_parameters.reward_period <= T::BlockNumber::from(3600),
  597. Error::InvalidStorageRoleParameterRewardPeriod
  598. );
  599. ensure!(
  600. role_parameters.bonding_period >= T::BlockNumber::from(600),
  601. Error::InvalidStorageRoleParameterBondingPeriod
  602. );
  603. ensure!(
  604. role_parameters.bonding_period <= T::BlockNumber::from(28800),
  605. Error::InvalidStorageRoleParameterBondingPeriod
  606. );
  607. ensure!(
  608. role_parameters.unbonding_period >= T::BlockNumber::from(600),
  609. Error::InvalidStorageRoleParameterUnbondingPeriod
  610. );
  611. ensure!(
  612. role_parameters.unbonding_period <= T::BlockNumber::from(28800),
  613. Error::InvalidStorageRoleParameterUnbondingPeriod
  614. );
  615. ensure!(
  616. role_parameters.min_service_period >= T::BlockNumber::from(600),
  617. Error::InvalidStorageRoleParameterMinServicePeriod
  618. );
  619. ensure!(
  620. role_parameters.min_service_period <= T::BlockNumber::from(28800),
  621. Error::InvalidStorageRoleParameterMinServicePeriod
  622. );
  623. ensure!(
  624. role_parameters.startup_grace_period >= T::BlockNumber::from(600),
  625. Error::InvalidStorageRoleParameterStartupGracePeriod
  626. );
  627. ensure!(
  628. role_parameters.startup_grace_period <= T::BlockNumber::from(28800),
  629. Error::InvalidStorageRoleParameterStartupGracePeriod
  630. );
  631. ensure!(
  632. role_parameters.min_stake > <BalanceOfGovernanceCurrency<T>>::from(0u32),
  633. Error::InvalidStorageRoleParameterMinStake
  634. );
  635. let max_min_stake: u32 = get_required_stake_by_fraction::<T>(1, 100)
  636. .try_into()
  637. .unwrap_or_default() as u32;
  638. ensure!(
  639. role_parameters.min_stake < <BalanceOfGovernanceCurrency<T>>::from(max_min_stake),
  640. Error::InvalidStorageRoleParameterMinStake
  641. );
  642. ensure!(
  643. role_parameters.entry_request_fee > <BalanceOfGovernanceCurrency<T>>::from(0u32),
  644. Error::InvalidStorageRoleParameterEntryRequestFee
  645. );
  646. let max_entry_request_fee: u32 = get_required_stake_by_fraction::<T>(1, 100)
  647. .try_into()
  648. .unwrap_or_default() as u32;
  649. ensure!(
  650. role_parameters.entry_request_fee
  651. < <BalanceOfGovernanceCurrency<T>>::from(max_entry_request_fee),
  652. Error::InvalidStorageRoleParameterEntryRequestFee
  653. );
  654. ensure!(
  655. role_parameters.reward > <BalanceOfGovernanceCurrency<T>>::from(0u32),
  656. Error::InvalidStorageRoleParameterReward
  657. );
  658. let max_reward: u32 = get_required_stake_by_fraction::<T>(1, 1000)
  659. .try_into()
  660. .unwrap_or_default() as u32;
  661. ensure!(
  662. role_parameters.reward < <BalanceOfGovernanceCurrency<T>>::from(max_reward),
  663. Error::InvalidStorageRoleParameterReward
  664. );
  665. Ok(())
  666. }
  667. /*
  668. entry_request_fee [tJOY] >0 <1% NA
  669. * Not enforced by runtime. Should not be displayed in the UI, or at least grayed out.
  670. ** Should not be displayed in the UI, or at least grayed out.
  671. */
  672. // validates council election parameters for the 'Set election parameters' proposal
  673. pub(crate) fn ensure_council_election_parameters_valid(
  674. election_parameters: &ElectionParameters<BalanceOfGovernanceCurrency<T>, T::BlockNumber>,
  675. ) -> Result<(), Error> {
  676. ensure!(
  677. election_parameters.council_size >= 4,
  678. Error::InvalidCouncilElectionParameterCouncilSize
  679. );
  680. ensure!(
  681. election_parameters.council_size <= 20,
  682. Error::InvalidCouncilElectionParameterCouncilSize
  683. );
  684. ensure!(
  685. election_parameters.candidacy_limit >= 25,
  686. Error::InvalidCouncilElectionParameterCandidacyLimit
  687. );
  688. ensure!(
  689. election_parameters.candidacy_limit <= 100,
  690. Error::InvalidCouncilElectionParameterCandidacyLimit
  691. );
  692. ensure!(
  693. election_parameters.min_voting_stake >= <BalanceOfGovernanceCurrency<T>>::one(),
  694. Error::InvalidCouncilElectionParameterMinVotingStake
  695. );
  696. ensure!(
  697. election_parameters.min_voting_stake
  698. <= <BalanceOfGovernanceCurrency<T>>::from(100000u32),
  699. Error::InvalidCouncilElectionParameterMinVotingStake
  700. );
  701. ensure!(
  702. election_parameters.new_term_duration >= T::BlockNumber::from(14400),
  703. Error::InvalidCouncilElectionParameterNewTermDuration
  704. );
  705. ensure!(
  706. election_parameters.new_term_duration <= T::BlockNumber::from(432000),
  707. Error::InvalidCouncilElectionParameterNewTermDuration
  708. );
  709. ensure!(
  710. election_parameters.revealing_period >= T::BlockNumber::from(14400),
  711. Error::InvalidCouncilElectionParameterRevealingPeriod
  712. );
  713. ensure!(
  714. election_parameters.revealing_period <= T::BlockNumber::from(43200),
  715. Error::InvalidCouncilElectionParameterRevealingPeriod
  716. );
  717. ensure!(
  718. election_parameters.voting_period >= T::BlockNumber::from(14400),
  719. Error::InvalidCouncilElectionParameterVotingPeriod
  720. );
  721. ensure!(
  722. election_parameters.voting_period <= T::BlockNumber::from(43200),
  723. Error::InvalidCouncilElectionParameterVotingPeriod
  724. );
  725. ensure!(
  726. election_parameters.announcing_period >= T::BlockNumber::from(14400),
  727. Error::InvalidCouncilElectionParameterAnnouncingPeriod
  728. );
  729. ensure!(
  730. election_parameters.announcing_period <= T::BlockNumber::from(43200),
  731. Error::InvalidCouncilElectionParameterAnnouncingPeriod
  732. );
  733. ensure!(
  734. election_parameters.min_council_stake >= <BalanceOfGovernanceCurrency<T>>::one(),
  735. Error::InvalidCouncilElectionParameterMinCouncilStake
  736. );
  737. ensure!(
  738. election_parameters.min_council_stake
  739. <= <BalanceOfGovernanceCurrency<T>>::from(100000u32),
  740. Error::InvalidCouncilElectionParameterMinCouncilStake
  741. );
  742. Ok(())
  743. }
  744. }
  745. // calculates required stake value using total issuance value and stake percentage. Truncates to
  746. // lowest integer value. Value fraction is defined by numerator and denominator.
  747. pub(crate) fn get_required_stake_by_fraction<T: crate::Trait>(
  748. numerator: u32,
  749. denominator: u32,
  750. ) -> BalanceOf<T> {
  751. let total_issuance: u128 = <CurrencyOf<T>>::total_issuance().try_into().unwrap_or(0) as u128;
  752. let required_stake =
  753. Perbill::from_rational_approximation(numerator, denominator) * total_issuance;
  754. let balance: BalanceOf<T> = required_stake.saturated_into();
  755. balance
  756. }