From f2c046c6cd922dbd785817919a89b784fb66158a Mon Sep 17 00:00:00 2001 From: Meyo Stpehen <90961928+letodunc@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:50:49 +0200 Subject: [PATCH] 98 voting pallet (#108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updating branch with main updates (#100) * Runtime update Update to polkadot-v0.9.26 * Token symbol and pallet-uniques Added the pallet-uniques to the runtime, and changed the token Symbol to USD * Benchmarking fixed Co-authored-by: ndkazu Co-authored-by: Kazunobu Ndong <33208377+ndkazu@users.noreply.github.com> * Merge main into voting pallet branch (#104) * Runtime update Update to polkadot-v0.9.26 * Token symbol and pallet-uniques Added the pallet-uniques to the runtime, and changed the token Symbol to USD * Benchmarking fixed * refresh FS repo (#101) * update readme and badges * delete diagram * add w3f badge and adjust readme * adjust size and position * remove unused files * adjusted the readme some more * add w3f badge back * changed from docker hub to ghcr registry * small change to the DockerFile * 102 automatization tasks (#103) * added a script to add a new pallet from a pallet template and configure the runtime Cargo.toml and lib.rs * added a script to update polkadot lib in .toml files for the node, runtime, pallets and pallet template Co-authored-by: ndkazu Co-authored-by: Kazunobu Ndong <33208377+ndkazu@users.noreply.github.com> Co-authored-by: Ilhan <29432367+ilhanu@users.noreply.github.com> Co-authored-by: “ilhanu” * added voting pallet, added config in runtime * added collective and democracy pallets to voting pallet and runtime config * added collective and democracy config to runtime * updated pallet_template * added members to house council * added functions to voting * added pallet roles to voting * update functions implementation * added events, refactored code * updated test configuration * added call formating for collective::propose() * added full process voting implementation with basic checks * added origin check on proposal dispatching from Democracy * added check origin call from collective pallet * added proposal status transition management and documentation * updated voting mockup for tests * refactored code * removed comments * code review updates * code review updates * fix for voting benchmarking * added fix for voting benchmarking * fix update * temporary deactivated voting benchmarking * added unit tests * added more tests * renamed house_council to council in configuration Co-authored-by: ndkazu Co-authored-by: Kazunobu Ndong <33208377+ndkazu@users.noreply.github.com> Co-authored-by: Ilhan <29432367+ilhanu@users.noreply.github.com> Co-authored-by: “ilhanu” --- node/src/chain_spec.rs | 4 +- pallets/voting/src/lib.rs | 14 +- pallets/voting/src/mock.rs | 60 ++- pallets/voting/src/tests.rs | 710 ++++++++++++++++++++++++++++++++++++ runtime/src/lib.rs | 2 +- 5 files changed, 770 insertions(+), 20 deletions(-) diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index dd27d2af..4e28b1ab 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -1,6 +1,6 @@ use fs_node_runtime::{ AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig, - SystemConfig, WASM_BINARY, HouseCouncilConfig + SystemConfig, WASM_BINARY, CouncilConfig }; use sc_service::Properties; use sc_service::ChainType; @@ -163,7 +163,7 @@ fn testnet_genesis( transaction_payment: Default::default(), democracy: Default::default(), treasury: Default::default(), - house_council: HouseCouncilConfig { + council: CouncilConfig { members: vec![ get_account_id_from_seed::("Alice"), get_account_id_from_seed::("Bob"), diff --git a/pallets/voting/src/lib.rs b/pallets/voting/src/lib.rs index a60fe2c7..a5af1f58 100644 --- a/pallets/voting/src/lib.rs +++ b/pallets/voting/src/lib.rs @@ -129,6 +129,8 @@ pub mod pallet { HouseCouncilVoted(T::AccountId, T::Hash, BlockNumberOf), /// A investor has voted InvestorVoted(T::AccountId, T::Hash, BlockNumberOf), + /// The investor vote session has started + InvestorVoteSessionStarted(T::Hash, BlockNumberOf), /// TODO: to remove, Event for test purpose CollectiveMotionChecked(BlockNumberOf), /// TODO: to remove, Event for test purpose @@ -253,7 +255,7 @@ pub mod pallet { let block_number = >::block_number(); - let collective_motion_duration = block_number.saturating_add(>::MotionDuration::get()).saturating_add(T::CheckDelay::get()); + let collective_motion_duration = block_number.saturating_add(>::MotionDuration::get()).saturating_add(T::Delay::get()); // Add the proposal to the collective watchlist CollectiveProposals::::insert(proposal_hash, collective_motion_duration); @@ -303,7 +305,8 @@ pub mod pallet { let delay = ::Delay::get(); // Start Democracy referendum - let referendum_index = DEMO::Pallet::::internal_start_referendum(proposal_hash, threshold,delay); + + let referendum_index = DEMO::Pallet::::internal_start_referendum(proposal_hash.clone(), threshold,delay); // Update the voting let mut proposal = VotingProposals::::get(proposal_id).unwrap(); @@ -323,6 +326,8 @@ pub mod pallet { // Execute the dispatch for collective vote passed proposal.collective_passed_call.dispatch(frame_system::RawOrigin::Signed(account_id).into()); + Self::deposit_event(Event::InvestorVoteSessionStarted(proposal_hash, block_number)); + Ok(().into()) } @@ -540,7 +545,8 @@ impl Pallet let mut collectives_hash = Vec::new(); for elt in collectives_iter { - if elt.1 < now { + if elt.1 <= now { + let voting = VotingProposals::::get(elt.0).unwrap(); if voting.collective_closed { @@ -564,7 +570,7 @@ impl Pallet let mut democracies_hash = Vec::new(); for elt in democracies_iter { - if elt.1 < now { + if elt.1 <= now { let voting = VotingProposals::::get(elt.0).unwrap(); if !voting.proposal_executed { diff --git a/pallets/voting/src/mock.rs b/pallets/voting/src/mock.rs index 5e5a31a0..f0e8f631 100644 --- a/pallets/voting/src/mock.rs +++ b/pallets/voting/src/mock.rs @@ -6,6 +6,7 @@ use frame_support::{ }; +use pallet_roles::GenesisBuild; use pallet_collective::PrimeDefaultVote; use frame_system::{EnsureRoot,EnsureSigned}; use frame_support::pallet_prelude::Weight; @@ -46,7 +47,7 @@ pub type BlockNumber = u64; pub type Balance = u128; parameter_types! { - pub const MotionDuration: u64 = 3; + pub const MotionDuration: u64 = 2; pub const MaxProposals: u32 = 100; pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max(1024); @@ -93,9 +94,9 @@ impl pallet_roles::Config for Test { parameter_types! { pub const Delay: BlockNumber = 0;//3 * MINUTES; - pub const CheckDelay: BlockNumber = 1 * 60_000;//3 * MINUTES; - pub const InvestorVoteAmount: u128 = 10 * 1000000; - pub const CheckPeriod: BlockNumber = 1 * 60_000; + pub const CheckDelay: BlockNumber = 1;//3 * MINUTES; + pub const InvestorVoteAmount: u128 = 1; + pub const CheckPeriod: BlockNumber = 1; } impl pallet_voting::Config for Test { @@ -112,12 +113,16 @@ impl pallet_voting::Config for Test { } +parameter_types! { + pub const CouncilMotionDuration: BlockNumber = 2; +} + type CouncilCollective = pallet_collective::Instance1; impl COLL::Config for Test { type Origin = Origin; type Proposal = Call; type Event = Event; - type MotionDuration = ConstU64<3>; + type MotionDuration = CouncilMotionDuration; type MaxProposals = MaxProposals; type MaxMembers = MaxMembers; type DefaultVote = PrimeDefaultVote; @@ -157,15 +162,15 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub const LaunchPeriod: BlockNumber = 5; //ok + pub const LaunchPeriod: BlockNumber = 1; //ok pub const VotingPeriod: BlockNumber = 5; //ok - pub const FastTrackVotingPeriod: BlockNumber = 2; //ok + pub const FastTrackVotingPeriod: BlockNumber = 20; //ok pub const InstantAllowed: bool = true; //ok - pub const MinimumDeposit: Balance = 100; //ok - pub const EnactmentPeriod: BlockNumber = 5; //ok - pub const CooloffPeriod: BlockNumber = 5; //ok - pub const PreimageByteDeposit: Balance = 1; //ok - pub const MaxVotes: u32 = 100; + pub const MinimumDeposit: Balance = 1; //ok + pub const EnactmentPeriod: BlockNumber = 200; //ok + pub const CooloffPeriod: BlockNumber = 200; //ok + pub const PreimageByteDeposit: Balance = 10; //ok + pub const MaxVotes: u32 = 4; } impl pallet_democracy::Config for Test { @@ -200,9 +205,38 @@ impl pallet_democracy::Config for Test { } +pub const ALICE: u64 = 1; +pub const BOB: u64 = 2; +pub const CHARLIE: u64 = 3; +pub const DAVE: u64 = 4; +pub const EVE: u64 = 5; // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() + // frame_system::GenesisConfig::default().build_storage::().unwrap().into() + + let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + // Initialize balances + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, 200_000 ), (BOB, 200_000_000 ), (CHARLIE, 200_000_000 ), (DAVE, 150_000), (EVE, 150_000 )], + } + .assimilate_storage(&mut storage) + .unwrap(); + + pallet_collective::GenesisConfig:: { + members: vec![ALICE, BOB, CHARLIE, DAVE], + phantom: Default::default(), + } + .assimilate_storage(&mut storage) + .unwrap(); + + pallet_sudo::GenesisConfig:: { key: Some(ALICE) } + .assimilate_storage(&mut storage) + .unwrap(); + + let mut externalities = sp_io::TestExternalities::new(storage); + externalities.execute_with(|| System::set_block_number(1)); + externalities } \ No newline at end of file diff --git a/pallets/voting/src/tests.rs b/pallets/voting/src/tests.rs index 413396a5..5e4d0877 100644 --- a/pallets/voting/src/tests.rs +++ b/pallets/voting/src/tests.rs @@ -1,5 +1,9 @@ use crate::{mock::*, Error}; use frame_support::{assert_noop, assert_ok}; +use codec::Encode; +use pallet_roles::Hash; +use pallet_roles::Hooks; +use pallet_democracy::Event; #[test] fn it_works_for_default_value() { @@ -7,3 +11,709 @@ fn it_works_for_default_value() { assert_eq!(Some(42), Some(42)); }); } + +fn make_proposal(value: i32) -> Box { + Box::new(Call::System(frame_system::Call::remark { remark: value.encode() })) +} + +#[test] +fn submit_proposal_not_seller_should_fail() { + new_test_ext().execute_with(|| { + assert_noop!( + VotingModule::submit_proposal( + Origin::signed(EVE), + make_proposal(1), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ), + Error::::NotASeller + ); + }); +} + +#[test] +fn submit_proposal_should_succeed() { + new_test_ext().execute_with(|| { + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + let hash = ::Hashing::hash_of(&proposal); + + assert_eq!( + VotingModule::voting_proposals(hash.clone()).is_some(), + true + ); + + let voting_proposal = VotingModule::voting_proposals(hash.clone()).unwrap(); + + assert_eq!(voting_proposal.account_id, EVE); + assert_eq!(voting_proposal.proposal_call, proposal.clone()); + assert_eq!(voting_proposal.collective_passed_call, make_proposal(2)); + assert_eq!(voting_proposal.collective_failed_call, make_proposal(3)); + assert_eq!(voting_proposal.democracy_failed_call, make_proposal(4)); + assert_eq!(voting_proposal.proposal_hash, hash.clone()); + assert_eq!(voting_proposal.collective_index, 0); + assert_eq!(voting_proposal.democracy_referendum_index, 0); + assert_eq!(voting_proposal.collective_step, false); + assert_eq!(voting_proposal.proposal_executed, false); + assert_eq!(voting_proposal.collective_closed, false); + + let block_number = System::block_number() + .saturating_add(::Delay::get()) + .saturating_add(>::MotionDuration::get()); + + assert_eq!( + VotingModule::collective_proposals(hash.clone()), + Some(block_number) + ); + + assert_eq!( + VotingModule::democracy_proposals(hash.clone()).is_none(), + true + ); + }); +} + +#[test] +fn council_vote_not_house_council_member_should_fail() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + let hash = ::Hashing::hash_of(&proposal); + + assert_noop!( + VotingModule::council_vote( + Origin::signed(EVE), + hash.clone(), + true, + ), + Error::::NotAHouseCouncilMember + ); + }); +} + +#[test] +fn council_vote_proposal_not_exist_should_fail() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + let hash = ::Hashing::hash_of(&proposal); + + assert_noop!( + VotingModule::council_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ), + Error::::ProposalDoesNotExist + ); + }); +} + +#[test] +fn council_vote_proposal_should_succeed() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + let hash = ::Hashing::hash_of(&proposal); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ) + ); + + let event = >::events() + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + crate::mock::Event::VotingModule(crate::Event::HouseCouncilVoted(ALICE, hash, 1)), + ); + }); +} + +#[test] +fn council_close_vote_not_house_council_member_should_fail() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + let hash = ::Hashing::hash_of(&proposal); + + assert_noop!( + VotingModule::council_close_vote( + Origin::signed(EVE), + hash.clone(), + ), + Error::::NotAHouseCouncilMember + ); + }); +} + +#[test] +fn council_close_vote_proposal_not_exist_should_fail() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + let hash = ::Hashing::hash_of(&proposal); + + assert_noop!( + VotingModule::council_close_vote( + Origin::signed(ALICE), + hash.clone(), + ), + Error::::ProposalDoesNotExist + ); + }); +} + +#[test] +fn council_close_vote_proposal_not_pass_should_succeed() { + new_test_ext().execute_with(|| { + + System::on_initialize(System::block_number()); + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + let initial_block_number = System::block_number(); + + let end_block_number = initial_block_number + .saturating_add(::Delay::get()) + .saturating_add(>::MotionDuration::get()); + + // We advance the time to reach the block number of the ending proposal vote period + System::set_block_number(end_block_number.clone()); + + let hash = ::Hashing::hash_of(&proposal); + + assert_ok!( + VotingModule::council_close_vote( + Origin::signed(ALICE), + hash.clone(), + ) + ); + + let voting_proposal = VotingModule::voting_proposals(hash.clone()).unwrap(); + + assert_eq!(voting_proposal.collective_closed, true); + assert_eq!(voting_proposal.collective_step, false); + + // Simulate the regular block check to have the update storage computation + VotingModule::begin_block(end_block_number.clone() + 1); + + assert_eq!( + VotingModule::collective_proposals(hash.clone()).is_none(), + true + ); + + let event = >::events() + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + crate::mock::Event::VotingModule(crate::Event::HouseCouncilClosedProposal(ALICE, hash, end_block_number)), + ); + }); +} + +#[test] +fn council_close_vote_proposal_pass_should_succeed() { + new_test_ext().execute_with(|| { + + System::on_initialize(System::block_number()); + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + let hash = ::Hashing::hash_of(&proposal); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(BOB), + hash.clone(), + true, + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(CHARLIE), + hash.clone(), + true, + ) + ); + + let initial_block_number = System::block_number(); + + let end_block_number = initial_block_number + .saturating_add(::Delay::get()) + .saturating_add(>::MotionDuration::get()); + + // We advance the time to reach the block number of the ending proposal vote period + System::set_block_number(end_block_number.clone()); + + assert_ok!( + VotingModule::council_close_vote( + Origin::signed(ALICE), + hash.clone(), + ) + ); + + let voting_proposal = VotingModule::voting_proposals(hash.clone()).unwrap(); + + assert_eq!(voting_proposal.collective_closed, true); + assert_eq!(voting_proposal.collective_step, true); + + // Simulate the regular block check to have the update storage computation + VotingModule::begin_block(end_block_number.clone() + 1); + + assert_eq!( + VotingModule::collective_proposals(hash.clone()).is_none(), + true + ); + + let end_democracy_vote = end_block_number + .saturating_add(::Delay::get()) + .saturating_add(::VotingPeriod::get()); + + assert_eq!( + VotingModule::democracy_proposals(hash.clone()), + Some(end_democracy_vote) + ); + + let mut events = >::events(); + let mut event = events + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + crate::mock::Event::VotingModule(crate::Event::HouseCouncilClosedProposal(ALICE, hash, end_block_number)), + ); + }); +} + +#[test] +fn investor_vote_without_having_invetsor_role_should_fail() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + let hash = ::Hashing::hash_of(&proposal); + + assert_noop!( + VotingModule::investor_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ), + Error::::NotAnInvestor + ); + }); +} + +#[test] +fn investor_vote_with_bad_proposal_should_fail() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(ALICE), + crate::ROLES::Accounts::INVESTOR + )); + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + let hash = ::Hashing::hash_of(&proposal); + + assert_noop!( + VotingModule::investor_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ), + Error::::ProposalDoesNotExist + ); + }); +} + +#[test] +fn investor_vote_should_succeed() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(ALICE), + crate::ROLES::Accounts::INVESTOR + )); + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + let hash = ::Hashing::hash_of(&proposal); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(BOB), + hash.clone(), + true, + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(CHARLIE), + hash.clone(), + true, + ) + ); + + let initial_block_number = System::block_number(); + + let end_block_number = initial_block_number + .saturating_add(::Delay::get()) + .saturating_add(>::MotionDuration::get()); + + // We advance the time to reach the block number of the ending proposal vote period + System::set_block_number(end_block_number.clone()); + + assert_ok!( + VotingModule::council_close_vote( + Origin::signed(ALICE), + hash.clone(), + ) + ); + + // Simulate the regular block check to have the update storage computation + VotingModule::begin_block(end_block_number.clone() + 1); + System::set_block_number(end_block_number.clone() + 1); + + assert_ok!( + VotingModule::investor_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ) + ); + + let mut event = >::events() + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + crate::mock::Event::VotingModule(crate::Event::InvestorVoted(ALICE, hash, System::block_number())), + ); + }); +} + +#[test] +fn investor_vote_proposal_fail_should_succeed() { + new_test_ext().execute_with(|| { + + // Give the investor role to the account + assert_ok!(RoleModule::set_role( + Origin::signed(ALICE), + crate::ROLES::Accounts::INVESTOR + )); + assert_ok!(RoleModule::set_role( + Origin::signed(EVE), + crate::ROLES::Accounts::SELLER + )); + + assert_ok!( + RoleModule::account_approval(Origin::signed(ALICE), EVE) + ); + + let proposal = make_proposal(1); + let hash = ::Hashing::hash_of(&proposal); + + assert_ok!( + VotingModule::submit_proposal( + Origin::signed(EVE), + proposal.clone(), + make_proposal(2), + make_proposal(3), + make_proposal(4) + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(ALICE), + hash.clone(), + true, + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(BOB), + hash.clone(), + true, + ) + ); + + assert_ok!( + VotingModule::council_vote( + Origin::signed(CHARLIE), + hash.clone(), + true, + ) + ); + + let initial_block_number = System::block_number(); + + let end_block_number = initial_block_number + .saturating_add(::Delay::get()) + .saturating_add(>::MotionDuration::get()); + + // We advance the time to reach the block number of the ending proposal vote period + System::set_block_number(end_block_number.clone()); + + assert_ok!( + VotingModule::council_close_vote( + Origin::signed(ALICE), + hash.clone(), + ) + ); + + // Simulate the regular block check to have the update storage computation + VotingModule::begin_block(end_block_number.clone() + 1); + Democracy::on_initialize(end_block_number.clone() + 1); + System::set_block_number(end_block_number.clone() + 1); + + assert_ok!( + VotingModule::investor_vote( + Origin::signed(ALICE), + hash.clone(), + false, + ) + ); + + let end_democracy_vote = end_block_number + .saturating_add(::Delay::get()) + .saturating_add(::VotingPeriod::get()); + + Democracy::on_initialize(end_democracy_vote.clone() + 1); + VotingModule::begin_block(end_democracy_vote.clone() + 2); + + assert_eq!( + VotingModule::democracy_proposals(hash.clone()).is_none(), + true + ); + + let mut event = >::events() + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + crate::mock::Event::Democracy(Event::NotPassed{ref_index: 1}), + ); + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 6d3794a6..f9ac5467 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -541,7 +541,7 @@ construct_runtime!( Treasury: pallet_treasury, Scheduler: pallet_scheduler, Preimage: pallet_preimage, - HouseCouncil: pallet_collective::, + Council: pallet_collective::, Democracy: pallet_democracy, // flag add pallet runtime }